diff --git a/.clang-tidy b/.clang-tidy index 04bb857609..07274eb53a 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,4 +1,6 @@ --- +# This entire group of checks was applied to all cpp files but not all header files. +# --- Checks: "-*, bugprone-argument-comment, bugprone-assert-side-effect, @@ -8,24 +10,26 @@ Checks: "-*, bugprone-chained-comparison, bugprone-compare-pointer-to-member-virtual-function, bugprone-copy-constructor-init, + # bugprone-crtp-constructor-accessibility, # has issues bugprone-dangling-handle, bugprone-dynamic-static-initializers, - bugprone-empty-catch, + # bugprone-empty-catch, # has issues bugprone-fold-init-type, - bugprone-forward-declaration-namespace, - bugprone-inaccurate-erase, - bugprone-incorrect-enable-if, - bugprone-incorrect-roundings, - bugprone-infinite-loop, - bugprone-integer-division, + # bugprone-forward-declaration-namespace, # has issues + # bugprone-inaccurate-erase, + # bugprone-inc-dec-in-conditions, + # bugprone-incorrect-enable-if, + # bugprone-incorrect-roundings, + # bugprone-infinite-loop, + # bugprone-integer-division, bugprone-lambda-function-name, - bugprone-macro-parentheses, + # bugprone-macro-parentheses, # has issues bugprone-macro-repeated-side-effects, bugprone-misplaced-operator-in-strlen-in-alloc, bugprone-misplaced-pointer-arithmetic-in-alloc, bugprone-misplaced-widening-cast, bugprone-move-forwarding-reference, - bugprone-multi-level-implicit-pointer-conversion, + # bugprone-multi-level-implicit-pointer-conversion, # has issues bugprone-multiple-new-in-one-expression, bugprone-multiple-statement-macro, bugprone-no-escape, @@ -35,13 +39,13 @@ Checks: "-*, bugprone-pointer-arithmetic-on-polymorphic-object, bugprone-posix-return, bugprone-redundant-branch-condition, - bugprone-reserved-identifier, - bugprone-return-const-ref-from-parameter, + # bugprone-reserved-identifier, # has issues + # bugprone-return-const-ref-from-parameter, # has issues bugprone-shared-ptr-array-mismatch, bugprone-signal-handler, bugprone-signed-char-misuse, bugprone-sizeof-container, - bugprone-sizeof-expression, + # bugprone-sizeof-expression, # has issues bugprone-spuriously-wake-up-functions, bugprone-standalone-empty, bugprone-string-constructor, @@ -58,85 +62,86 @@ Checks: "-*, bugprone-suspicious-string-compare, bugprone-suspicious-stringview-data-usage, bugprone-swapped-arguments, + # bugprone-switch-missing-default-case, # has issues bugprone-terminating-continue, bugprone-throw-keyword-missing, bugprone-too-small-loop-variable, + # bugprone-unchecked-optional-access, # see https://github.com/XRPLF/rippled/pull/6502 bugprone-undefined-memory-manipulation, bugprone-undelegated-constructor, bugprone-unhandled-exception-at-new, + bugprone-unhandled-self-assignment, bugprone-unique-ptr-array-mismatch, bugprone-unsafe-functions, + # bugprone-use-after-move, # has issues + bugprone-unused-raii, + bugprone-unused-return-value, bugprone-unused-local-non-trivial-variable, bugprone-virtual-near-miss, + cppcoreguidelines-init-variables, + cppcoreguidelines-misleading-capture-default-by-value, cppcoreguidelines-no-suspend-with-lock, + cppcoreguidelines-pro-type-member-init, + cppcoreguidelines-pro-type-static-cast-downcast, + cppcoreguidelines-rvalue-reference-param-not-moved, + cppcoreguidelines-use-default-member-init, cppcoreguidelines-virtual-class-destructor, hicpp-ignored-remove-result, + misc-const-correctness, misc-definitions-in-headers, misc-header-include-cycle, misc-misplaced-const, + misc-redundant-expression, misc-static-assert, misc-throw-by-value-catch-by-reference, misc-unused-alias-decls, misc-unused-using-decls, - readability-duplicate-include, - readability-enum-initial-value, - readability-misleading-indentation, - readability-non-const-parameter, - readability-redundant-declaration, - readability-reference-to-constructed-temporary, modernize-deprecated-headers, modernize-make-shared, modernize-make-unique, + llvm-namespace-comment, + performance-faster-string-find, + performance-for-range-copy, performance-implicit-conversion-in-loop, + performance-inefficient-vector-operation, + performance-move-const-arg, performance-move-constructor-init, - performance-trivially-destructible + performance-no-automatic-move, + performance-trivially-destructible, + # readability-avoid-nested-conditional-operator, # has issues + # readability-avoid-return-with-void-value, # has issues + # readability-braces-around-statements, # has issues + # readability-const-return-type, # has issues + # readability-container-contains, # has issues + # readability-container-size-empty, # has issues + # readability-convert-member-functions-to-static, # has issues + readability-duplicate-include, + # readability-else-after-return, # has issues + # readability-enum-initial-value, # has issues + # readability-implicit-bool-conversion, # has issues + # readability-make-member-function-const, # has issues + # readability-math-missing-parentheses, # has issues + readability-misleading-indentation, + readability-non-const-parameter, + # readability-redundant-casting, # has issues + # readability-redundant-declaration, # has issues + # readability-redundant-inline-specifier, # has issues + # readability-redundant-member-init, # has issues + readability-redundant-string-init, + readability-reference-to-constructed-temporary, + # readability-simplify-boolean-expr, # has issues + # readability-static-definition-in-anonymous-namespace, # has issues + # readability-suspicious-call-argument, # has issues + readability-use-std-min-max " # --- -# checks that have some issues that need to be resolved: +# other checks that have issues that need to be resolved: # -# bugprone-crtp-constructor-accessibility, -# bugprone-inc-dec-in-conditions, -# bugprone-move-forwarding-reference, -# bugprone-switch-missing-default-case, -# bugprone-unused-return-value, -# bugprone-use-after-move, -# bugprone-unhandled-self-assignment, -# bugprone-unused-raii, -# -# cppcoreguidelines-misleading-capture-default-by-value, -# cppcoreguidelines-init-variables, -# cppcoreguidelines-pro-type-member-init, -# cppcoreguidelines-pro-type-static-cast-downcast, -# cppcoreguidelines-use-default-member-init, -# cppcoreguidelines-rvalue-reference-param-not-moved, -# -# llvm-namespace-comment, -# misc-const-correctness, # misc-include-cleaner, -# misc-redundant-expression, # -# readability-avoid-nested-conditional-operator, -# readability-avoid-return-with-void-value, -# readability-braces-around-statements, -# readability-container-contains, -# readability-container-size-empty, -# readability-convert-member-functions-to-static, -# readability-const-return-type, -# readability-else-after-return, -# readability-implicit-bool-conversion, -# readability-inconsistent-declaration-parameter-name, -# readability-identifier-naming, -# readability-make-member-function-const, -# readability-math-missing-parentheses, -# readability-redundant-inline-specifier, -# readability-redundant-member-init, -# readability-redundant-casting, -# readability-redundant-string-init, -# readability-simplify-boolean-expr, -# readability-static-definition-in-anonymous-namespace, -# readability-suspicious-call-argument, -# readability-use-std-min-max, -# readability-static-accessed-through-instance, +# 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-identifier-naming, # https://github.com/XRPLF/rippled/pull/6571 # # modernize-concat-nested-namespaces, # modernize-pass-by-value, @@ -150,16 +155,10 @@ Checks: "-*, # modernize-use-starts-ends-with, # modernize-use-std-numbers, # modernize-use-using, -# -# performance-faster-string-find, -# performance-for-range-copy, -# performance-inefficient-vector-operation, -# performance-move-const-arg, -# performance-no-automatic-move, # --- # CheckOptions: - # readability-braces-around-statements.ShortStatementLines: 2 + readability-braces-around-statements.ShortStatementLines: 2 # readability-identifier-naming.MacroDefinitionCase: UPPER_CASE # readability-identifier-naming.ClassCase: CamelCase # readability-identifier-naming.StructCase: CamelCase @@ -194,8 +193,9 @@ CheckOptions: # readability-identifier-naming.PublicMemberSuffix: "" # readability-identifier-naming.FunctionIgnoredRegexp: ".*tag_invoke.*" bugprone-unsafe-functions.ReportMoreUnsafeFunctions: true -# bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc + bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc # misc-include-cleaner.IgnoreHeaders: '.*/(detail|impl)/.*;.*(expected|unexpected).*;.*ranges_lower_bound\.h;time.h;stdlib.h;__chrono/.*;fmt/chrono.h;boost/uuid/uuid_hash.hpp' # -# HeaderFilterRegex: '^.*/(src|tests)/.*\.(h|hpp)$' +HeaderFilterRegex: '^.*/(test|xrpl|xrpld)/.*\.(h|hpp)$' +ExcludeHeaderFilterRegex: '^.*/protocol_autogen/.*\.(h|hpp)$' WarningsAsErrors: "*" diff --git a/.gersemi/definitions.cmake b/.gersemi/definitions.cmake index 13061629a4..a16e330ffa 100644 --- a/.gersemi/definitions.cmake +++ b/.gersemi/definitions.cmake @@ -51,6 +51,9 @@ endfunction() function(add_module parent name) endfunction() +function(setup_protocol_autogen) +endfunction() + function(target_link_modules parent scope) endfunction() diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index cf50d48f95..0cf704b051 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,16 +1,79 @@ # This feature requires Git >= 2.24 # To use it by default in git blame: # git config blame.ignoreRevsFile .git-blame-ignore-revs -50760c693510894ca368e90369b0cc2dabfd07f3 -e2384885f5f630c8f0ffe4bf21a169b433a16858 -241b9ddde9e11beb7480600fd5ed90e1ef109b21 -760f16f56835663d9286bd29294d074de26a7ba6 -0eebe6a5f4246fced516d52b83ec4e7f47373edd -2189cc950c0cebb89e4e2fa3b2d8817205bf7cef -b9d007813378ad0ff45660dc07285b823c7e9855 -fe9a5365b8a52d4acc42eb27369247e6f238a4f9 -9a93577314e6a8d4b4a8368cc9d2b15a5d8303e8 -552377c76f55b403a1c876df873a23d780fcc81c -97f0747e103f13e26e45b731731059b32f7679ac -b13370ac0d207217354f1fc1c29aef87769fb8a1 + +# This file is sorted in reverse chronological order, with the most recent commits at the top. +# The commits listed here are ignored by git blame, which is useful for formatting-only commits that would otherwise obscure the history of changes to a file. + +# refactor: Enable remaining clang-tidy `cppcoreguidelines` checks (#6538) +72f4cb097f626b08b02fc3efcb4aa11cb2e7adb8 +# refactor: Rename system name from 'ripple' to 'xrpld' (#6347) +ffea3977f0b771fe8e43a8f74e4d393d63a7afd8 +# refactor: Update transaction folder structure (#6483) +5865bd017f777491b4a956f9210be0c4161f5442 +# chore: Use gersemi instead of ancient cmake-format (#6486) +0c74270b055133a57a497b5c9fc5a75f7647b1f4 +# chore: Apply clang-format width 100 (#6387) +2c1fad102353e11293e3edde1c043224e7d3e983 +# chore: Set clang-format width to 100 in config file (#6387) +25cca465538a56cce501477f9e5e2c1c7ea2d84c +# chore: Set cmake-format width to 100 (#6386) +469ce9f291a4480c38d4ee3baca5136b2f053cd0 +# refactor: Modularize app/tx (#6228) +0976b2b68b64972af8e6e7c497900b5bce9fe22f +# chore: Update clang-format to 21.1.8 (#6352) +958d8f375453d80bb1aa4c293b5102c045a3e4b4 +# refactor: Replace include guards by '#pragma once' (#6322) +34ef577604782ca8d6e1c17df8bd7470990a52ff +# chore: Format all cmake files without comments (#6294) +fe9c8d568fcf6ac21483024e01f58962dd5c8260 +# chore: Add cmake-format pre-commit hook (#6279) +a0e09187b9370805d027c611a7e9ff5a0125282a +# chore: Set ColumnLimit to 120 in clang-format (#6288) +5f638f55536def0d88b970d1018a465a238e55f4 +# refactor: Fix typos in comments, configure cspell (#6164) +3c9f5b62525cb1d6ca1153eeb10433db7d7379fd +# refactor: Rename `rippled.cfg` to `xrpld.cfg` (#6098) +3d1b3a49b3601a0a7037fa0b19d5df7b5e0e2fc1 +# refactor: Rename `ripple` namespace to `xrpl` (#5982) +1eb0fdac6543706b4b9ddca57fd4102928a1f871 +# refactor: Rename `rippled` binary to `xrpld` (#5983) +9eb84a561ef8bb066d89f098bd9b4ac71baed67c +# refactor: Replaces secp256k1 source by Conan package (#6089) +813bc4d9491b078bb950f8255f93b02f71320478 +# refactor: Remove unnecessary copyright notices already covered by LICENSE.md (#5929) +1d42c4f6de6bf01d1286fc7459b17a37a5189e88 +# refactor: Rename `RIPPLE_` and `RIPPLED_` definitions to `XRPL_` (#5821) +ada83564d894829424b0f4d922b0e737e07abbf7 +# refactor: Modularize shamap and nodestore (#5668) +8eb233c2ea8ad5a159be73b77f0f5e1496d547ac +# refactor: Modularise ledger (#5493) +dc8b37a52448b005153c13a7f046ad494128cf94 +# chore: Update clang-format and prettier with pre-commit (#5709) +c14ce956adeabe476ad73c18d73103f347c9c613 +# chore: Fix file formatting (#5718) 896b8c3b54a22b0497cb0d1ce95e1095f9a227ce +# chore: Reverts formatting changes to external files, adds formatting changes to proto files (#5711) +b13370ac0d207217354f1fc1c29aef87769fb8a1 +# chore: Run prettier on all files (#5657) +97f0747e103f13e26e45b731731059b32f7679ac +# Reformat code with clang-format-18 +552377c76f55b403a1c876df873a23d780fcc81c +# Recompute loops (#4997) +d028005aa6319338b0adae1aebf8abe113162960 +# Rewrite includes (#4997) +1d23148e6dd53957fcb6205c07a5c6cd7b64d50c +# Rearrange sources (#4997) +e416ee72ca26fa0c09d2aee1b68bdfb2b7046eed +# Move CMake directory (#4997) +2e902dee53aab2a8f27f32971047bb81e022f94f +# Rewrite includes +0eebe6a5f4246fced516d52b83ec4e7f47373edd +# Format formerly .hpp files +760f16f56835663d9286bd29294d074de26a7ba6 +# Rename .hpp to .h +241b9ddde9e11beb7480600fd5ed90e1ef109b21 +# Consolidate external libraries +e2384885f5f630c8f0ffe4bf21a169b433a16858 +# Format first-party source according to .clang-format +50760c693510894ca368e90369b0cc2dabfd07f3 diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 967b3c1817..05e87d16e0 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,7 +1,7 @@ --- name: Feature Request -about: Suggest a new feature for the rippled project -title: "[Title with short description] (Version: [rippled version])" +about: Suggest a new feature for the xrpld project +title: "[Title with short description] (Version: [xrpld version])" labels: Feature Request assignees: "" --- diff --git a/.github/actions/generate-version/action.yml b/.github/actions/generate-version/action.yml index 6b84aac2f3..8edb7920c6 100644 --- a/.github/actions/generate-version/action.yml +++ b/.github/actions/generate-version/action.yml @@ -11,7 +11,7 @@ runs: steps: # When a tag is pushed, the version is used as-is. - name: Generate version for tag event - if: ${{ github.event_name == 'tag' }} + if: ${{ startsWith(github.ref, 'refs/tags/') }} shell: bash env: VERSION: ${{ github.ref_name }} @@ -22,7 +22,7 @@ runs: # We use a plus sign instead of a hyphen because Conan recipe versions do # not support two hyphens. - name: Generate version for non-tag event - if: ${{ github.event_name != 'tag' }} + if: ${{ !startsWith(github.ref, 'refs/tags/') }} shell: bash run: | echo 'Extracting version from BuildInfo.cpp.' diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3ab3a38807..f1f7aa18f7 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -29,22 +29,6 @@ If a refactor, how is this better than the previous implementation? If there is a spec or design document for this feature, please link it here. --> -### Type of Change - - - -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] Refactor (non-breaking change that only restructures code) -- [ ] Performance (increase or change in throughput and/or latency) -- [ ] Tests (you added tests for code that already exists, or your new feature included in this PR) -- [ ] Documentation update -- [ ] Chore (no impact to binary, e.g. `.gitignore`, formatting, dropping support for older tooling) -- [ ] Release - ### API Impact ripple state entry - SLE::ref sleAccount, // --> the account being set. - bool const bAuth, // --> authorize account. - bool const bNoRipple, // --> others cannot ripple through - bool const bFreeze, // --> funds cannot leave - bool bDeepFreeze, // --> can neither receive nor send funds - STAmount const& saBalance, // --> balance of account being set. - // Issuer should be noAccount() - STAmount const& saLimit, // --> limit for account being set. - // Issuer should be the account being set. - std::uint32_t uSrcQualityIn, - std::uint32_t uSrcQualityOut, - beast::Journal j); - -[[nodiscard]] TER -removeEmptyHolding( - ApplyView& view, - AccountID const& accountID, - Issue const& issue, - beast::Journal journal); - -[[nodiscard]] TER -removeEmptyHolding( - ApplyView& view, - AccountID const& accountID, - MPTIssue const& mptIssue, - beast::Journal journal); - -[[nodiscard]] inline TER -removeEmptyHolding( - ApplyView& view, - AccountID const& accountID, - Asset const& asset, - beast::Journal journal) -{ - return std::visit( - [&](TIss const& issue) -> TER { - return removeEmptyHolding(view, accountID, issue, journal); - }, - asset.value()); -} - -[[nodiscard]] TER -trustDelete( - ApplyView& view, - std::shared_ptr const& sleRippleState, - AccountID const& uLowAccountID, - AccountID const& uHighAccountID, - beast::Journal j); - -/** Delete an offer. - - Requirements: - The passed `sle` be obtained from a prior - call to view.peek() -*/ -// [[nodiscard]] // nodiscard commented out so Flow, BookTip and others compile. -TER -offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j); - -//------------------------------------------------------------------------------ - -// -// Money Transfers -// - -// Direct send w/o fees: -// - Redeeming IOUs and/or sending sender's own IOUs. -// - Create trust line of needed. -// --> bCheckIssuer : normally require issuer to be involved. -// [[nodiscard]] // nodiscard commented out so DirectStep.cpp compiles. - -/** Calls static rippleCreditIOU if saAmount represents Issue. - * Calls static rippleCreditMPT if saAmount represents MPTIssue. - */ -TER -rippleCredit( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - bool bCheckIssuer, - beast::Journal j); - -TER -rippleLockEscrowMPT( - ApplyView& view, - AccountID const& uGrantorID, - STAmount const& saAmount, - beast::Journal j); - -TER -rippleUnlockEscrowMPT( - ApplyView& view, - AccountID const& uGrantorID, - AccountID const& uGranteeID, - STAmount const& netAmount, - STAmount const& grossAmount, - beast::Journal j); - -/** Calls static accountSendIOU if saAmount represents Issue. - * Calls static accountSendMPT if saAmount represents MPTIssue. - */ -[[nodiscard]] TER -accountSend( - ApplyView& view, - AccountID const& from, - AccountID const& to, - STAmount const& saAmount, - beast::Journal j, - WaiveTransferFee waiveFee = WaiveTransferFee::No); - -using MultiplePaymentDestinations = std::vector>; -/** Like accountSend, except one account is sending multiple payments (with the - * same asset!) simultaneously - * - * Calls static accountSendMultiIOU if saAmount represents Issue. - * Calls static accountSendMultiMPT if saAmount represents MPTIssue. - */ -[[nodiscard]] TER -accountSendMulti( - ApplyView& view, - AccountID const& senderID, - Asset const& asset, - MultiplePaymentDestinations const& receivers, - beast::Journal j, - WaiveTransferFee waiveFee = WaiveTransferFee::No); - -[[nodiscard]] TER -issueIOU( - ApplyView& view, - AccountID const& account, - STAmount const& amount, - Issue const& issue, - beast::Journal j); - -[[nodiscard]] TER -redeemIOU( - ApplyView& view, - AccountID const& account, - STAmount const& amount, - Issue const& issue, - beast::Journal j); - -[[nodiscard]] TER -transferXRP( - ApplyView& view, - AccountID const& from, - AccountID const& to, - STAmount const& amount, - beast::Journal j); - -/* Check if MPToken (for MPT) or trust line (for IOU) exists: - * - StrongAuth - before checking if authorization is required - * - WeakAuth - * for MPT - after checking lsfMPTRequireAuth flag - * for IOU - do not check if trust line exists - * - Legacy - * for MPT - before checking lsfMPTRequireAuth flag i.e. same as StrongAuth - * for IOU - do not check if trust line exists i.e. same as WeakAuth - */ -enum class AuthType { StrongAuth, WeakAuth, Legacy }; - -/** Check if the account lacks required authorization. - * - * Return tecNO_AUTH or tecNO_LINE if it does - * and tesSUCCESS otherwise. - * - * If StrongAuth then return tecNO_LINE if the RippleState doesn't exist. Return - * tecNO_AUTH if lsfRequireAuth is set on the issuer's AccountRoot, and the - * RippleState does exist, and the RippleState is not authorized. - * - * If WeakAuth then return tecNO_AUTH if lsfRequireAuth is set, and the - * RippleState exists, and is not authorized. Return tecNO_LINE if - * lsfRequireAuth is set and the RippleState doesn't exist. Consequently, if - * WeakAuth and lsfRequireAuth is *not* set, this function will return - * tesSUCCESS even if RippleState does *not* exist. - * - * The default "Legacy" auth type is equivalent to WeakAuth. - */ -[[nodiscard]] TER -requireAuth( - ReadView const& view, - Issue const& issue, - AccountID const& account, - AuthType authType = AuthType::Legacy); - -/** Check if the account lacks required authorization. - * - * This will also check for expired credentials. If it is called directly - * from preclaim, the user should convert result tecEXPIRED to tesSUCCESS and - * proceed to also check permissions with enforceMPTokenAuthorization inside - * doApply. This will ensure that any expired credentials are deleted. - * - * requireAuth check is recursive for MPT shares in a vault, descending to - * assets in the vault, up to maxAssetCheckDepth recursion depth. This is - * purely defensive, as we currently do not allow such vaults to be created. - * - * If StrongAuth then return tecNO_AUTH if MPToken doesn't exist or - * lsfMPTRequireAuth is set and MPToken is not authorized. Vault and LoanBroker - * pseudo-accounts are implicitly authorized. - * - * If WeakAuth then return tecNO_AUTH if lsfMPTRequireAuth is set and MPToken - * doesn't exist or is not authorized (explicitly or via credentials, if - * DomainID is set in MPTokenIssuance). Consequently, if WeakAuth and - * lsfMPTRequireAuth is *not* set, this function will return true even if - * MPToken does *not* exist. - * - * The default "Legacy" auth type is equivalent to StrongAuth. - */ -[[nodiscard]] TER -requireAuth( - ReadView const& view, - MPTIssue const& mptIssue, - AccountID const& account, - AuthType authType = AuthType::Legacy, - int depth = 0); - -[[nodiscard]] TER inline requireAuth( - ReadView const& view, - Asset const& asset, - AccountID const& account, - AuthType authType = AuthType::Legacy) -{ - return std::visit( - [&](TIss const& issue_) { - return requireAuth(view, issue_, account, authType); - }, - asset.value()); -} - -/** Enforce account has MPToken to match its authorization. - * - * Called from doApply - it will check for expired (and delete if found any) - * credentials matching DomainID set in MPTokenIssuance. Must be called if - * requireAuth(...MPTIssue...) returned tesSUCCESS or tecEXPIRED in preclaim, - * which implies that preclaim should replace `tecEXPIRED` with `tesSUCCESS` - * in order for the transactor to proceed to doApply. - * - * This function will create MPToken (if needed) on the basis of any - * non-expired credentials and will delete any expired credentials, indirectly - * via verifyValidDomain, as per DomainID (if set in MPTokenIssuance). - * - * The caller does NOT need to ensure that DomainID is actually set - this - * function handles gracefully both cases when DomainID is set and when not. - * - * The caller does NOT need to look for existing MPToken to match - * mptIssue/account - this function checks lsfMPTAuthorized of an existing - * MPToken iff DomainID is not set. - * - * Do not use for accounts which hold implied permission e.g. object owners or - * if MPTokenIssuance does not require authorization. In both cases use - * MPTokenAuthorize::authorize if MPToken does not yet exist. - */ -[[nodiscard]] TER -enforceMPTokenAuthorization( - ApplyView& view, - MPTID const& mptIssuanceID, - AccountID const& account, - 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. - */ -[[nodiscard]] TER -canTransfer( - ReadView const& view, - MPTIssue const& mptIssue, - AccountID const& from, - AccountID const& to); - -[[nodiscard]] TER -canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, AccountID const& to); - -[[nodiscard]] TER inline canTransfer( - ReadView const& view, - Asset const& asset, - AccountID const& from, - AccountID const& to) -{ - return std::visit( - [&](TIss const& issue) -> TER { - return canTransfer(view, issue, from, to); - }, - asset.value()); -} - /** Deleter function prototype. Returns the status of the entry deletion * (if should not be skipped) and if the entry should be skipped. The status * is always tesSUCCESS if the entry should be skipped. @@ -1052,57 +232,6 @@ cleanupOnAccountDelete( beast::Journal j, std::optional maxNodesToDelete = std::nullopt); -/** Delete trustline to AMM. The passed `sle` must be obtained from a prior - * call to view.peek(). Fail if neither side of the trustline is AMM or - * if ammAccountID is seated and is not one of the trustline's side. - */ -[[nodiscard]] TER -deleteAMMTrustLine( - ApplyView& view, - std::shared_ptr sleState, - std::optional const& ammAccountID, - beast::Journal j); - -// From the perspective of a vault, return the number of shares to give the -// depositor when they deposit a fixed amount of assets. Since shares are MPT -// this number is integral and always truncated in this calculation. -[[nodiscard]] std::optional -assetsToSharesDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& assets); - -// From the perspective of a vault, return the number of assets to take from -// depositor when they receive a fixed amount of shares. Note, since shares are -// MPT, they are always an integral number. -[[nodiscard]] std::optional -sharesToAssetsDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& shares); - -enum class TruncateShares : bool { no = false, yes = true }; - -// From the perspective of a vault, return the number of shares to demand from -// the depositor when they ask to withdraw a fixed amount of assets. Since -// shares are MPT this number is integral, and it will be rounded to nearest -// unless explicitly requested to be truncated instead. -[[nodiscard]] std::optional -assetsToSharesWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& assets, - TruncateShares truncate = TruncateShares::no); - -// From the perspective of a vault, return the number of assets to give the -// depositor when they redeem a fixed amount of shares. Note, since shares are -// MPT, they are always an integral number. -[[nodiscard]] std::optional -sharesToAssetsWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& shares); - /** Has the specified time passed? @param now the current time diff --git a/include/xrpl/tx/transactors/dex/AMMHelpers.h b/include/xrpl/ledger/helpers/AMMHelpers.h similarity index 96% rename from include/xrpl/tx/transactors/dex/AMMHelpers.h rename to include/xrpl/ledger/helpers/AMMHelpers.h index 1db9176538..55f27a3f06 100644 --- a/include/xrpl/tx/transactors/dex/AMMHelpers.h +++ b/include/xrpl/ledger/helpers/AMMHelpers.h @@ -22,7 +22,7 @@ reduceOffer(auto const& amount) static Number const reducedOfferPct(9999, -4); // Make sure the result is always less than amount or zero. - NumberRoundModeGuard mg(Number::towards_zero); + NumberRoundModeGuard const mg(Number::towards_zero); return amount * reducedOfferPct; } @@ -121,7 +121,6 @@ withinRelativeDistance(Quality const& calcQuality, Quality const& reqQuality, Nu * @param dist requested relative distance * @return true if within dist, false otherwise */ -// clang-format off template requires( std::is_same_v || std::is_same_v || @@ -134,7 +133,6 @@ withinRelativeDistance(Amt const& calc, Amt const& req, Number const& dist) auto const [min, max] = std::minmax(calc, req); return ((max - min) / max) < dist; } -// clang-format on /** Solve quadratic equation to find takerGets or takerPays. Round * to minimize the amount in order to maximize the quality. @@ -175,7 +173,7 @@ getAMMOfferStartWithTakerGets( if (targetQuality.rate() == beast::zero) return std::nullopt; - NumberRoundModeGuard mg(Number::to_nearest); + NumberRoundModeGuard const mg(Number::to_nearest); auto const f = feeMult(tfee); auto const a = 1; auto const b = pool.in * (1 - 1 / f) / targetQuality.rate() - 2 * pool.out; @@ -203,7 +201,7 @@ getAMMOfferStartWithTakerGets( // Try to reduce the offer size to improve the quality. // The quality might still not match the targetQuality for a tiny offer. - if (auto const amounts = getAmounts(*nTakerGets); Quality{amounts} < targetQuality) + if (auto amounts = getAmounts(*nTakerGets); Quality{amounts} < targetQuality) return getAmounts(detail::reduceOffer(amounts.out)); else return amounts; @@ -242,7 +240,7 @@ getAMMOfferStartWithTakerPays( if (targetQuality.rate() == beast::zero) return std::nullopt; - NumberRoundModeGuard mg(Number::to_nearest); + NumberRoundModeGuard const mg(Number::to_nearest); auto const f = feeMult(tfee); auto const& a = f; auto const b = pool.in * (1 + f); @@ -270,7 +268,7 @@ getAMMOfferStartWithTakerPays( // Try to reduce the offer size to improve the quality. // The quality might still not match the targetQuality for a tiny offer. - if (auto const amounts = getAmounts(*nTakerPays); Quality{amounts} < targetQuality) + if (auto amounts = getAmounts(*nTakerPays); Quality{amounts} < targetQuality) return getAmounts(detail::reduceOffer(amounts.in)); else return amounts; @@ -335,8 +333,7 @@ changeSpotPriceQuality( } auto const takerPays = toAmount(getIssue(pool.in), nTakerPays, Number::upward); // should not fail - if (auto const amounts = - TAmounts{takerPays, swapAssetIn(pool, takerPays, tfee)}; + if (auto amounts = TAmounts{takerPays, swapAssetIn(pool, takerPays, tfee)}; Quality{amounts} < quality && !withinRelativeDistance(Quality{amounts}, quality, Number(1, -7))) { @@ -362,7 +359,7 @@ changeSpotPriceQuality( // Generate the offer starting with XRP side. Return seated offer amounts // if the offer can be generated, otherwise nullopt. - auto const amounts = [&]() { + auto amounts = [&]() { if (isXRP(getIssue(pool.out))) return getAMMOfferStartWithTakerGets(pool, quality, tfee); return getAMMOfferStartWithTakerPays(pool, quality, tfee); @@ -370,7 +367,7 @@ changeSpotPriceQuality( if (!amounts) { JLOG(j.trace()) << "changeSpotPrice calc failed: " << to_string(pool.in) << " " - << to_string(pool.out) << " " << quality << " " << tfee << std::endl; + << to_string(pool.out) << " " << quality << " " << tfee; return std::nullopt; } @@ -438,7 +435,7 @@ swapAssetIn(TAmounts const& pool, TIn const& assetIn, std::uint16_t t // 1-fee // maximize: // fee - saveNumberRoundMode _{Number::getround()}; + saveNumberRoundMode const _{Number::getround()}; Number::setround(Number::upward); auto const numerator = pool.in * pool.out; @@ -502,7 +499,7 @@ swapAssetOut(TAmounts const& pool, TOut const& assetOut, std::uint16_ // maximize: // tfee/100000 - saveNumberRoundMode _{Number::getround()}; + saveNumberRoundMode const _{Number::getround()}; Number::setround(Number::upward); auto const numerator = pool.in * pool.out; @@ -639,9 +636,9 @@ getRoundedAsset(Rules const& rules, STAmount const& balance, A const& frac, IsDe STAmount getRoundedAsset( Rules const& rules, - std::function&& noRoundCb, + std::function const& noRoundCb, STAmount const& balance, - std::function&& productCb, + std::function const& productCb, IsDeposit isDeposit); /** Round AMM deposit/withdrawal LPToken amount. Deposit/withdrawal formulas @@ -672,9 +669,9 @@ getRoundedLPTokens( STAmount getRoundedLPTokens( Rules const& rules, - std::function&& noRoundCb, + std::function const& noRoundCb, STAmount const& lptAMMBalance, - std::function&& productCb, + std::function const& productCb, IsDeposit isDeposit); /* Next two functions adjust asset in/out amount to factor in the adjusted diff --git a/include/xrpl/tx/transactors/dex/AMMUtils.h b/include/xrpl/ledger/helpers/AMMUtils.h similarity index 98% rename from include/xrpl/tx/transactors/dex/AMMUtils.h rename to include/xrpl/ledger/helpers/AMMUtils.h index 18db7e6555..77ad18106e 100644 --- a/include/xrpl/tx/transactors/dex/AMMUtils.h +++ b/include/xrpl/ledger/helpers/AMMUtils.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h new file mode 100644 index 0000000000..353c27fe41 --- /dev/null +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -0,0 +1,112 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace xrpl { + +/** Check if the issuer has the global freeze flag set. + @param issuer The account to check + @return true if the account has global freeze set +*/ +[[nodiscard]] bool +isGlobalFrozen(ReadView const& view, AccountID const& issuer); + +// Calculate liquid XRP balance for an account. +// This function may be used to calculate the amount of XRP that +// the holder is able to freely spend. It subtracts reserve requirements. +// +// ownerCountAdj adjusts the owner count in case the caller calculates +// before ledger entries are added or removed. Positive to add, negative +// to subtract. +// +// @param ownerCountAdj positive to add to count, negative to reduce count. +[[nodiscard]] XRPAmount +xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j); + +/** Adjust the owner count up or down. */ +void +adjustOwnerCount( + ApplyView& view, + std::shared_ptr const& 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 + * is represented as 1,010,000,000. + * @param issuer The IOU issuer + */ +[[nodiscard]] Rate +transferRate(ReadView const& view, AccountID const& issuer); + +/** Generate a pseudo-account address from a pseudo owner key. + @param pseudoOwnerKey The key to generate the address from + @return The generated account ID +*/ +AccountID +pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey); + +/** Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account + if set. + + The list is constructed during initialization and is const after that. + Pseudo-account designator fields MUST be maintained by including the + SField::sMD_PseudoAccount flag in the SField definition. +*/ +[[nodiscard]] std::vector const& +getPseudoAccountFields(); + +/** Returns true if and only if sleAcct is a pseudo-account or specific + pseudo-accounts in pseudoFieldFilter. + + Returns false if sleAcct is: + - NOT a pseudo-account OR + - NOT a ltACCOUNT_ROOT OR + - null pointer +*/ +[[nodiscard]] bool +isPseudoAccount( + std::shared_ptr sleAcct, + std::set const& pseudoFieldFilter = {}); + +/** Convenience overload that reads the account from the view. */ +[[nodiscard]] inline bool +isPseudoAccount( + ReadView const& view, + AccountID const& accountId, + std::set const& pseudoFieldFilter = {}) +{ + return isPseudoAccount(view.read(keylet::account(accountId)), pseudoFieldFilter); +} + +/** + * Create pseudo-account, storing pseudoOwnerKey into ownerField. + * + * The list of valid ownerField is maintained in AccountRootHelpers.cpp and + * the caller to this function must perform necessary amendment check(s) + * before using a field. The amendment check is **not** performed in + * createPseudoAccount. + */ +[[nodiscard]] Expected, TER> +createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField); + +/** Checks the destination and tag. + + - Checks that the SLE is not null. + - If the SLE requires a destination tag, checks that there is a tag. +*/ +[[nodiscard]] TER +checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag); + +} // namespace xrpl diff --git a/include/xrpl/ledger/CredentialHelpers.h b/include/xrpl/ledger/helpers/CredentialHelpers.h similarity index 95% rename from include/xrpl/ledger/CredentialHelpers.h rename to include/xrpl/ledger/helpers/CredentialHelpers.h index 3744ff47eb..c60bd6c7fa 100644 --- a/include/xrpl/ledger/CredentialHelpers.h +++ b/include/xrpl/ledger/helpers/CredentialHelpers.h @@ -49,7 +49,7 @@ validDomain(ReadView const& view, uint256 domainID, AccountID const& subject); // This function is only called when we about to return tecNO_PERMISSION // because all the checks for the DepositPreauth authorization failed. TER -authorizedDepositPreauth(ApplyView const& view, STVector256 const& ctx, AccountID const& dst); +authorizedDepositPreauth(ReadView const& view, STVector256 const& ctx, AccountID const& dst); // Sort credentials array, return empty set if there are duplicates std::set> @@ -74,7 +74,7 @@ verifyDepositPreauth( ApplyView& view, AccountID const& src, AccountID const& dst, - std::shared_ptr const& sleDst, + std::shared_ptr const& sleDst, beast::Journal j); } // namespace xrpl diff --git a/include/xrpl/tx/transactors/delegate/DelegateUtils.h b/include/xrpl/ledger/helpers/DelegateHelpers.h similarity index 100% rename from include/xrpl/tx/transactors/delegate/DelegateUtils.h rename to include/xrpl/ledger/helpers/DelegateHelpers.h diff --git a/include/xrpl/ledger/helpers/DirectoryHelpers.h b/include/xrpl/ledger/helpers/DirectoryHelpers.h new file mode 100644 index 0000000000..189dfcd263 --- /dev/null +++ b/include/xrpl/ledger/helpers/DirectoryHelpers.h @@ -0,0 +1,223 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace xrpl { + +namespace detail { + +template < + class V, + class N, + class = std::enable_if_t< + std::is_same_v, SLE> && std::is_base_of_v>> +bool +internalDirNext( + V& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry) +{ + auto const& svIndexes = page->getFieldV256(sfIndexes); + XRPL_ASSERT(index <= svIndexes.size(), "xrpl::detail::internalDirNext : index inside range"); + + if (index >= svIndexes.size()) + { + auto const next = page->getFieldU64(sfIndexNext); + + if (!next) + { + entry.zero(); + return false; + } + + if constexpr (std::is_const_v) + { + page = view.read(keylet::page(root, next)); + } + else + { + page = view.peek(keylet::page(root, next)); + } + + XRPL_ASSERT(page, "xrpl::detail::internalDirNext : non-null root"); + + if (!page) + return false; + + index = 0; + + return internalDirNext(view, root, page, index, entry); + } + + entry = svIndexes[index++]; + return true; +} + +template < + class V, + class N, + class = std::enable_if_t< + std::is_same_v, SLE> && std::is_base_of_v>> +bool +internalDirFirst( + V& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry) +{ + if constexpr (std::is_const_v) + { + page = view.read(keylet::page(root)); + } + else + { + page = view.peek(keylet::page(root)); + } + + if (!page) + return false; + + index = 0; + + return internalDirNext(view, root, page, index, entry); +} + +} // namespace detail + +/** @{ */ +/** Returns the first entry in the directory, advancing the index + + @deprecated These are legacy function that are considered deprecated + and will soon be replaced with an iterator-based model + that is easier to use. You should not use them in new code. + + @param view The view against which to operate + @param root The root (i.e. first page) of the directory to iterate + @param page The current page + @param index The index inside the current page + @param entry The entry at the current index + + @return true if the directory isn't empty; false otherwise + */ +bool +cdirFirst( + ReadView const& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry); + +bool +dirFirst( + ApplyView& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry); +/** @} */ + +/** @{ */ +/** Returns the next entry in the directory, advancing the index + + @deprecated These are legacy function that are considered deprecated + and will soon be replaced with an iterator-based model + that is easier to use. You should not use them in new code. + + @param view The view against which to operate + @param root The root (i.e. first page) of the directory to iterate + @param page The current page + @param index The index inside the current page + @param entry The entry at the current index + + @return true if the directory isn't empty; false otherwise + */ +bool +cdirNext( + ReadView const& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry); + +bool +dirNext( + ApplyView& view, + uint256 const& root, + std::shared_ptr& 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); + +/** Iterate all items after an item in the given directory. + @param after The key of the item to start after + @param hint The directory page containing `after` + @param limit The maximum number of items to return + @return `false` if the iteration failed +*/ +bool +forEachItemAfter( + ReadView const& view, + Keylet const& root, + uint256 const& after, + std::uint64_t const hint, + unsigned int limit, + std::function const&)> 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) +{ + return forEachItem(view, keylet::ownerDir(id), f); +} + +/** Iterate all items after an item in an owner directory. + @param after The key of the item to start after + @param hint The directory page containing `after` + @param limit The maximum number of items to return + @return `false` if the iteration failed +*/ +inline bool +forEachItemAfter( + ReadView const& view, + AccountID const& id, + uint256 const& after, + std::uint64_t const hint, + unsigned int limit, + std::function const&)> const& f) +{ + return forEachItemAfter(view, keylet::ownerDir(id), after, hint, limit, f); +} + +/** Returns `true` if the directory is empty + @param key The key of the directory +*/ +[[nodiscard]] bool +dirIsEmpty(ReadView const& view, Keylet const& k); + +/** Returns a function that sets the owner on a directory SLE */ +[[nodiscard]] std::function +describeOwnerDir(AccountID const& account); + +} // namespace xrpl diff --git a/include/xrpl/ledger/helpers/EscrowHelpers.h b/include/xrpl/ledger/helpers/EscrowHelpers.h new file mode 100644 index 0000000000..1680dbcad4 --- /dev/null +++ b/include/xrpl/ledger/helpers/EscrowHelpers.h @@ -0,0 +1,228 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +TER +escrowUnlockApplyHelper( + ApplyView& view, + Rate lockedRate, + std::shared_ptr const& sleDest, + STAmount const& xrpBalance, + STAmount const& amount, + AccountID const& issuer, + AccountID const& sender, + AccountID const& receiver, + bool createAsset, + beast::Journal journal); + +template <> +inline TER +escrowUnlockApplyHelper( + ApplyView& view, + Rate lockedRate, + std::shared_ptr const& sleDest, + STAmount const& xrpBalance, + STAmount const& amount, + AccountID const& issuer, + AccountID const& sender, + AccountID const& receiver, + bool createAsset, + beast::Journal journal) +{ + Keylet const trustLineKey = keylet::line(receiver, amount.issue()); + bool const recvLow = issuer > receiver; + bool const senderIssuer = issuer == sender; + bool const receiverIssuer = issuer == receiver; + + if (senderIssuer) + return tecINTERNAL; // LCOV_EXCL_LINE + + if (receiverIssuer) + return tesSUCCESS; + + if (!view.exists(trustLineKey) && createAsset) + { + // Can the account cover the trust line's reserve? + if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)}; + xrpBalance < view.fees().accountReserve(ownerCount + 1)) + { + JLOG(journal.trace()) << "Trust line does not exist. " + "Insufficient reserve to create line."; + + return tecNO_LINE_INSUF_RESERVE; + } + + Currency const currency = amount.getCurrency(); + STAmount initialBalance(amount.issue()); + initialBalance.setIssuer(noAccount()); + + if (TER const ter = trustCreate( + view, // payment sandbox + recvLow, // is dest low? + issuer, // source + receiver, // destination + trustLineKey.key, // ledger index + sleDest, // Account to add to + false, // authorize account + (sleDest->getFlags() & lsfDefaultRipple) == 0, // + false, // freeze trust line + false, // deep freeze trust line + initialBalance, // zero initial balance + Issue(currency, receiver), // limit of zero + 0, // quality in + 0, // quality out + journal); // journal + !isTesSuccess(ter)) + { + return ter; // LCOV_EXCL_LINE + } + + view.update(sleDest); + } + + if (!view.exists(trustLineKey) && !receiverIssuer) + return tecNO_LINE; + + auto const xferRate = transferRate(view, amount); + // update if issuer rate is less than locked rate + if (xferRate < lockedRate) + lockedRate = xferRate; + + // Transfer Rate only applies when: + // 1. Issuer is not involved in the transfer (senderIssuer or + // receiverIssuer) + // 2. The locked rate is different from the parity rate + + // NOTE: Transfer fee in escrow works a bit differently from a normal + // payment. In escrow, the fee is deducted from the locked/sending amount, + // whereas in a normal payment, the transfer fee is taken on top of the + // sending amount. + auto finalAmt = amount; + if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate) + { + // compute transfer fee, if any + auto const xferFee = amount.value() - divideRound(amount, lockedRate, amount.issue(), true); + // compute balance to transfer + finalAmt = amount.value() - xferFee; + } + + // validate the line limit if the account submitting txn is not the receiver + // of the funds + if (!createAsset) + { + auto const sleRippleState = view.peek(trustLineKey); + if (!sleRippleState) + return tecINTERNAL; // LCOV_EXCL_LINE + + // if the issuer is the high, then we use the low limit + // otherwise we use the high limit + STAmount const lineLimit = + sleRippleState->getFieldAmount(recvLow ? sfLowLimit : sfHighLimit); + + STAmount lineBalance = sleRippleState->getFieldAmount(sfBalance); + + // flip the sign of the line balance if the issuer is not high + if (!recvLow) + lineBalance.negate(); + + // add the final amount to the line balance + lineBalance += finalAmt; + + // if the transfer would exceed the line limit return tecLIMIT_EXCEEDED + if (lineLimit < lineBalance) + return tecLIMIT_EXCEEDED; + } + + // if destination is not the issuer then transfer funds + if (!receiverIssuer) + { + auto const ter = directSendNoFee(view, issuer, receiver, finalAmt, true, journal); + if (!isTesSuccess(ter)) + return ter; // LCOV_EXCL_LINE + } + return tesSUCCESS; +} + +template <> +inline TER +escrowUnlockApplyHelper( + ApplyView& view, + Rate lockedRate, + std::shared_ptr const& sleDest, + STAmount const& xrpBalance, + STAmount const& amount, + AccountID const& issuer, + AccountID const& sender, + AccountID const& receiver, + bool createAsset, + beast::Journal journal) +{ + bool const senderIssuer = issuer == sender; + bool const receiverIssuer = issuer == receiver; + + auto const mptID = amount.get().getMptID(); + auto const issuanceKey = keylet::mptIssuance(mptID); + if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && createAsset && !receiverIssuer) + { + if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)}; + xrpBalance < view.fees().accountReserve(ownerCount + 1)) + { + return tecINSUFFICIENT_RESERVE; + } + + if (auto const ter = createMPToken(view, mptID, receiver, 0); !isTesSuccess(ter)) + { + return ter; // LCOV_EXCL_LINE + } + + // update owner count. + adjustOwnerCount(view, sleDest, 1, journal); + } + + if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && !receiverIssuer) + return tecNO_PERMISSION; + + auto const xferRate = transferRate(view, amount); + // update if issuer rate is less than locked rate + if (xferRate < lockedRate) + lockedRate = xferRate; + + // Transfer Rate only applies when: + // 1. Issuer is not involved in the transfer (senderIssuer or + // receiverIssuer) + // 2. The locked rate is different from the parity rate + + // NOTE: Transfer fee in escrow works a bit differently from a normal + // payment. In escrow, the fee is deducted from the locked/sending amount, + // whereas in a normal payment, the transfer fee is taken on top of the + // sending amount. + auto finalAmt = amount; + if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate) + { + // compute transfer fee, if any + auto const xferFee = amount.value() - divideRound(amount, lockedRate, amount.asset(), true); + // compute balance to transfer + finalAmt = amount.value() - xferFee; + } + return unlockEscrowMPT( + view, + sender, + receiver, + finalAmt, + view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt, + journal); +} + +} // namespace xrpl diff --git a/include/xrpl/ledger/helpers/MPTokenHelpers.h b/include/xrpl/ledger/helpers/MPTokenHelpers.h new file mode 100644 index 0000000000..466486306c --- /dev/null +++ b/include/xrpl/ledger/helpers/MPTokenHelpers.h @@ -0,0 +1,167 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +//------------------------------------------------------------------------------ +// +// Freeze checking (MPT-specific) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] bool +isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue); + +[[nodiscard]] bool +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); + +[[nodiscard]] bool +isAnyFrozen( + ReadView const& view, + std::initializer_list const& accounts, + MPTIssue const& mptIssue, + int depth = 0); + +//------------------------------------------------------------------------------ +// +// Transfer rate (MPT-specific) +// +//------------------------------------------------------------------------------ + +/** Returns MPT transfer fee as Rate. Rate specifies + * the fee as fractions of 1 billion. For example, 1% transfer rate + * is represented as 1,010,000,000. + * @param issuanceID MPTokenIssuanceID of MPTTokenIssuance object + */ +[[nodiscard]] Rate +transferRate(ReadView const& view, MPTID const& issuanceID); + +//------------------------------------------------------------------------------ +// +// Holding checks (MPT-specific) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] TER +canAddHolding(ReadView const& view, MPTIssue const& mptIssue); + +//------------------------------------------------------------------------------ +// +// Authorization (MPT-specific) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] TER +authorizeMPToken( + ApplyView& view, + XRPAmount const& priorBalance, + MPTID const& mptIssuanceID, + AccountID const& account, + beast::Journal journal, + std::uint32_t flags = 0, + std::optional holderID = std::nullopt); + +/** Check if the account lacks required authorization for MPT. + * + * requireAuth check is recursive for MPT shares in a vault, descending to + * assets in the vault, up to maxAssetCheckDepth recursion depth. This is + * purely defensive, as we currently do not allow such vaults to be created. + */ +[[nodiscard]] TER +requireAuth( + ReadView const& view, + MPTIssue const& mptIssue, + AccountID const& account, + AuthType authType = AuthType::Legacy, + int depth = 0); + +/** Enforce account has MPToken to match its authorization. + * + * Called from doApply - it will check for expired (and delete if found any) + * credentials matching DomainID set in MPTokenIssuance. Must be called if + * requireAuth(...MPTIssue...) returned tesSUCCESS or tecEXPIRED in preclaim. + */ +[[nodiscard]] TER +enforceMPTokenAuthorization( + ApplyView& view, + MPTID const& mptIssuanceID, + AccountID const& account, + 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. + */ +[[nodiscard]] TER +canTransfer( + ReadView const& view, + MPTIssue const& mptIssue, + AccountID const& from, + AccountID const& to); + +//------------------------------------------------------------------------------ +// +// Empty holding operations (MPT-specific) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] TER +addEmptyHolding( + ApplyView& view, + AccountID const& accountID, + XRPAmount priorBalance, + MPTIssue const& mptIssue, + beast::Journal journal); + +[[nodiscard]] TER +removeEmptyHolding( + ApplyView& view, + AccountID const& accountID, + MPTIssue const& mptIssue, + beast::Journal journal); + +//------------------------------------------------------------------------------ +// +// Escrow operations (MPT-specific) +// +//------------------------------------------------------------------------------ + +TER +lockEscrowMPT( + ApplyView& view, + AccountID const& uGrantorID, + STAmount const& saAmount, + beast::Journal j); + +TER +unlockEscrowMPT( + ApplyView& view, + AccountID const& uGrantorID, + AccountID const& uGranteeID, + STAmount const& netAmount, + STAmount const& grossAmount, + beast::Journal j); + +TER +createMPToken( + ApplyView& view, + MPTID const& mptIssuanceID, + AccountID const& account, + std::uint32_t const flags); + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/nft/NFTokenUtils.h b/include/xrpl/ledger/helpers/NFTokenHelpers.h similarity index 94% rename from include/xrpl/tx/transactors/nft/NFTokenUtils.h rename to include/xrpl/ledger/helpers/NFTokenHelpers.h index a5e2b30354..d8dac4caaf 100644 --- a/include/xrpl/tx/transactors/nft/NFTokenUtils.h +++ b/include/xrpl/ledger/helpers/NFTokenHelpers.h @@ -1,11 +1,12 @@ #pragma once +#include #include #include #include #include +#include #include -#include namespace xrpl { @@ -54,7 +55,7 @@ removeToken( ApplyView& view, AccountID const& owner, uint256 const& nftokenID, - std::shared_ptr&& page); + std::shared_ptr const& page); /** Deletes the given token offer. @@ -95,7 +96,7 @@ tokenOfferCreatePreflight( std::uint16_t nftFlags, Rules const& rules, std::optional const& owner = std::nullopt, - std::uint32_t txFlags = lsfSellNFToken); + std::uint32_t txFlags = tfSellNFToken); /** Preclaim checks shared by NFTokenCreateOffer and NFTokenMint */ TER @@ -109,7 +110,7 @@ tokenOfferCreatePreclaim( std::uint16_t xferFee, beast::Journal j, std::optional const& owner = std::nullopt, - std::uint32_t txFlags = lsfSellNFToken); + std::uint32_t txFlags = tfSellNFToken); /** doApply implementation shared by NFTokenCreateOffer and NFTokenMint */ TER @@ -123,7 +124,7 @@ tokenOfferCreateApply( uint256 const& nftokenID, XRPAmount const& priorBalance, beast::Journal j, - std::uint32_t txFlags = lsfSellNFToken); + std::uint32_t txFlags = tfSellNFToken); TER checkTrustlineAuthorized( diff --git a/include/xrpl/ledger/helpers/OfferHelpers.h b/include/xrpl/ledger/helpers/OfferHelpers.h new file mode 100644 index 0000000000..9096071811 --- /dev/null +++ b/include/xrpl/ledger/helpers/OfferHelpers.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace xrpl { + +/** Delete an offer. + + Requirements: + The offer must exist. + The caller must have already checked permissions. + + @param view The ApplyView to modify. + @param sle The offer to delete. + @param j Journal for logging. + + @return tesSUCCESS on success, otherwise an error code. +*/ +// [[nodiscard]] // nodiscard commented out so Flow, BookTip and others compile. +TER +offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j); + +} // namespace xrpl diff --git a/include/xrpl/ledger/helpers/PaymentChannelHelpers.h b/include/xrpl/ledger/helpers/PaymentChannelHelpers.h new file mode 100644 index 0000000000..5d1a4c0aa1 --- /dev/null +++ b/include/xrpl/ledger/helpers/PaymentChannelHelpers.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace xrpl { + +TER +closeChannel( + std::shared_ptr const& slep, + ApplyView& view, + uint256 const& key, + beast::Journal j); + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/dex/PermissionedDEXHelpers.h b/include/xrpl/ledger/helpers/PermissionedDEXHelpers.h similarity index 100% rename from include/xrpl/tx/transactors/dex/PermissionedDEXHelpers.h rename to include/xrpl/ledger/helpers/PermissionedDEXHelpers.h diff --git a/include/xrpl/ledger/helpers/RippleStateHelpers.h b/include/xrpl/ledger/helpers/RippleStateHelpers.h new file mode 100644 index 0000000000..3feba59d1f --- /dev/null +++ b/include/xrpl/ledger/helpers/RippleStateHelpers.h @@ -0,0 +1,255 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// +// RippleState (Trustline) helpers +// +//------------------------------------------------------------------------------ + +namespace xrpl { + +//------------------------------------------------------------------------------ +// +// Credit functions (from Credit.h) +// +//------------------------------------------------------------------------------ + +/** Calculate the maximum amount of IOUs that an account can hold + @param view the ledger to check against. + @param account the account of interest. + @param issuer the issuer of the IOU. + @param currency the IOU to check. + @return The maximum amount that can be held. +*/ +/** @{ */ +STAmount +creditLimit( + ReadView const& view, + AccountID const& account, + AccountID const& issuer, + Currency const& currency); + +IOUAmount +creditLimit2(ReadView const& v, AccountID const& acc, AccountID const& iss, Currency const& cur); +/** @} */ + +/** Returns the amount of IOUs issued by issuer that are held by an account + @param view the ledger to check against. + @param account the account of interest. + @param issuer the issuer of the IOU. + @param currency the IOU to check. +*/ +/** @{ */ +STAmount +creditBalance( + ReadView const& view, + AccountID const& account, + AccountID const& issuer, + Currency const& currency); +/** @} */ + +//------------------------------------------------------------------------------ +// +// Freeze checking (IOU-specific) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] bool +isIndividualFrozen( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer); + +[[nodiscard]] inline bool +isIndividualFrozen(ReadView const& view, AccountID const& account, Issue const& issue) +{ + return isIndividualFrozen(view, account, issue.currency, issue.account); +} + +[[nodiscard]] bool +isFrozen( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer); + +[[nodiscard]] inline bool +isFrozen(ReadView const& view, AccountID const& account, Issue const& issue) +{ + return isFrozen(view, account, issue.currency, issue.account); +} + +// 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*/) +{ + return isFrozen(view, account, issue); +} + +[[nodiscard]] bool +isDeepFrozen( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer); + +[[nodiscard]] inline bool +isDeepFrozen( + ReadView const& view, + AccountID const& account, + Issue const& issue, + int = 0 /*ignored*/) +{ + return isDeepFrozen(view, account, issue.currency, issue.account); +} + +[[nodiscard]] inline TER +checkDeepFrozen(ReadView const& view, AccountID const& account, Issue const& issue) +{ + return isDeepFrozen(view, account, issue) ? (TER)tecFROZEN : (TER)tesSUCCESS; +} + +//------------------------------------------------------------------------------ +// +// Trust line operations +// +//------------------------------------------------------------------------------ + +/** Create a trust line + + This can set an initial balance. +*/ +[[nodiscard]] TER +trustCreate( + ApplyView& view, + bool const bSrcHigh, + AccountID const& uSrcAccountID, + AccountID const& uDstAccountID, + uint256 const& uIndex, // --> ripple state entry + SLE::ref sleAccount, // --> the account being set. + bool const bAuth, // --> authorize account. + bool const bNoRipple, // --> others cannot ripple through + bool const bFreeze, // --> funds cannot leave + bool bDeepFreeze, // --> can neither receive nor send funds + STAmount const& saBalance, // --> balance of account being set. + // Issuer should be noAccount() + STAmount const& saLimit, // --> limit for account being set. + // Issuer should be the account being set. + std::uint32_t uQualityIn, + std::uint32_t uQualityOut, + beast::Journal j); + +[[nodiscard]] TER +trustDelete( + ApplyView& view, + std::shared_ptr const& sleRippleState, + AccountID const& uLowAccountID, + AccountID const& uHighAccountID, + beast::Journal j); + +//------------------------------------------------------------------------------ +// +// IOU issuance/redemption +// +//------------------------------------------------------------------------------ + +[[nodiscard]] TER +issueIOU( + ApplyView& view, + AccountID const& account, + STAmount const& amount, + Issue const& issue, + beast::Journal j); + +[[nodiscard]] TER +redeemIOU( + ApplyView& view, + AccountID const& account, + STAmount const& amount, + Issue const& issue, + beast::Journal j); + +//------------------------------------------------------------------------------ +// +// Authorization and transfer checks (IOU-specific) +// +//------------------------------------------------------------------------------ + +/** Check if the account lacks required authorization. + * + * Return tecNO_AUTH or tecNO_LINE if it does + * and tesSUCCESS otherwise. + * + * If StrongAuth then return tecNO_LINE if the RippleState doesn't exist. Return + * tecNO_AUTH if lsfRequireAuth is set on the issuer's AccountRoot, and the + * RippleState does exist, and the RippleState is not authorized. + * + * If WeakAuth then return tecNO_AUTH if lsfRequireAuth is set, and the + * RippleState exists, and is not authorized. Return tecNO_LINE if + * lsfRequireAuth is set and the RippleState doesn't exist. Consequently, if + * WeakAuth and lsfRequireAuth is *not* set, this function will return + * tesSUCCESS even if RippleState does *not* exist. + * + * The default "Legacy" auth type is equivalent to WeakAuth. + */ +[[nodiscard]] TER +requireAuth( + ReadView const& view, + Issue const& issue, + AccountID const& account, + AuthType authType = AuthType::Legacy); + +/** Check if the destination account is allowed + * to receive IOU. Return terNO_RIPPLE if rippling is + * disabled on both sides and tesSUCCESS otherwise. + */ +[[nodiscard]] TER +canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, AccountID const& to); + +//------------------------------------------------------------------------------ +// +// Empty holding operations (IOU-specific) +// +//------------------------------------------------------------------------------ + +/// Any transactors that call addEmptyHolding() in doApply must call +/// canAddHolding() in preflight with the same View and Asset +[[nodiscard]] TER +addEmptyHolding( + ApplyView& view, + AccountID const& accountID, + XRPAmount priorBalance, + Issue const& issue, + beast::Journal journal); + +[[nodiscard]] TER +removeEmptyHolding( + ApplyView& view, + AccountID const& accountID, + Issue const& issue, + beast::Journal journal); + +/** Delete trustline to AMM. The passed `sle` must be obtained from a prior + * call to view.peek(). Fail if neither side of the trustline is AMM or + * if ammAccountID is seated and is not one of the trustline's side. + */ +[[nodiscard]] TER +deleteAMMTrustLine( + ApplyView& view, + std::shared_ptr sleState, + std::optional const& ammAccountID, + beast::Journal j); + +} // namespace xrpl diff --git a/include/xrpl/ledger/helpers/TokenHelpers.h b/include/xrpl/ledger/helpers/TokenHelpers.h new file mode 100644 index 0000000000..908950a1e1 --- /dev/null +++ b/include/xrpl/ledger/helpers/TokenHelpers.h @@ -0,0 +1,286 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +//------------------------------------------------------------------------------ +// +// Enums for token handling +// +//------------------------------------------------------------------------------ + +/** Controls the treatment of frozen account balances */ +enum FreezeHandling { fhIGNORE_FREEZE, fhZERO_IF_FROZEN }; + +/** Controls the treatment of unauthorized MPT balances */ +enum AuthHandling { ahIGNORE_AUTH, ahZERO_IF_UNAUTHORIZED }; + +/** Controls whether to include the account's full spendable balance */ +enum SpendableHandling { shSIMPLE_BALANCE, shFULL_BALANCE }; + +enum class WaiveTransferFee : bool { No = false, Yes }; + +/* Check if MPToken (for MPT) or trust line (for IOU) exists: + * - StrongAuth - before checking if authorization is required + * - WeakAuth + * for MPT - after checking lsfMPTRequireAuth flag + * for IOU - do not check if trust line exists + * - Legacy + * for MPT - before checking lsfMPTRequireAuth flag i.e. same as StrongAuth + * for IOU - do not check if trust line exists i.e. same as WeakAuth + */ +enum class AuthType { StrongAuth, WeakAuth, Legacy }; + +//------------------------------------------------------------------------------ +// +// Freeze checking (Asset-based dispatchers) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] bool +isGlobalFrozen(ReadView const& view, Asset const& asset); + +[[nodiscard]] bool +isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset); + +/** + * isFrozen check is recursive for MPT shares in a vault, descending to + * assets in the vault, up to maxAssetCheckDepth recursion depth. This is + * purely defensive, as we currently do not allow such vaults to be created. + */ +[[nodiscard]] bool +isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0); + +[[nodiscard]] TER +checkFrozen(ReadView const& view, AccountID const& account, Issue const& issue); + +[[nodiscard]] TER +checkFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue); + +[[nodiscard]] TER +checkFrozen(ReadView const& view, AccountID const& account, Asset const& asset); + +[[nodiscard]] bool +isAnyFrozen( + ReadView const& view, + std::initializer_list const& accounts, + Issue const& issue); + +[[nodiscard]] bool +isAnyFrozen( + ReadView const& view, + std::initializer_list const& accounts, + Asset const& asset, + int depth = 0); + +[[nodiscard]] bool +isDeepFrozen( + ReadView const& view, + AccountID const& account, + MPTIssue const& mptIssue, + int depth = 0); + +/** + * isFrozen check is recursive for MPT shares in a vault, descending to + * assets in the vault, up to maxAssetCheckDepth recursion depth. This is + * purely defensive, as we currently do not allow such vaults to be created. + */ +[[nodiscard]] bool +isDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0); + +[[nodiscard]] TER +checkDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue); + +[[nodiscard]] TER +checkDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset); + +//------------------------------------------------------------------------------ +// +// Account balance functions (Asset-based dispatchers) +// +//------------------------------------------------------------------------------ + +// Returns the amount an account can spend. +// +// If shSIMPLE_BALANCE is specified, this is the amount the account can spend +// without going into debt. +// +// If shFULL_BALANCE is specified, this is the amount the account can spend +// total. Specifically: +// * The account can go into debt if using a trust line, and the other side has +// a non-zero limit. +// * If the account is the asset issuer the limit is defined by the asset / +// issuance. +// +// <-- saAmount: amount of currency held by account. May be negative. +[[nodiscard]] STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer, + FreezeHandling zeroIfFrozen, + beast::Journal j, + SpendableHandling includeFullBalance = shSIMPLE_BALANCE); + +[[nodiscard]] STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + Issue const& issue, + FreezeHandling zeroIfFrozen, + beast::Journal j, + SpendableHandling includeFullBalance = shSIMPLE_BALANCE); + +[[nodiscard]] STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + MPTIssue const& mptIssue, + FreezeHandling zeroIfFrozen, + AuthHandling zeroIfUnauthorized, + beast::Journal j, + SpendableHandling includeFullBalance = shSIMPLE_BALANCE); + +[[nodiscard]] STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + Asset const& asset, + FreezeHandling zeroIfFrozen, + AuthHandling zeroIfUnauthorized, + beast::Journal j, + SpendableHandling includeFullBalance = shSIMPLE_BALANCE); + +// Returns the amount an account can spend of the currency type saDefault, or +// returns saDefault if this account is the issuer of the currency in +// question. Should be used in favor of accountHolds when questioning how much +// an account can spend while also allowing currency issuers to spend +// unlimited amounts of their own currency (since they can always issue more). +[[nodiscard]] STAmount +accountFunds( + ReadView const& view, + AccountID const& id, + STAmount const& saDefault, + FreezeHandling freezeHandling, + beast::Journal j); + +/** Returns the transfer fee as Rate based on the type of token + * @param view The ledger view + * @param amount The amount to transfer + */ +[[nodiscard]] Rate +transferRate(ReadView const& view, STAmount const& amount); + +//------------------------------------------------------------------------------ +// +// Holding operations (Asset-based dispatchers) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] TER +canAddHolding(ReadView const& view, Asset const& asset); + +[[nodiscard]] TER +addEmptyHolding( + ApplyView& view, + AccountID const& accountID, + XRPAmount priorBalance, + Asset const& asset, + beast::Journal journal); + +[[nodiscard]] TER +removeEmptyHolding( + ApplyView& view, + AccountID const& accountID, + Asset const& asset, + beast::Journal journal); + +//------------------------------------------------------------------------------ +// +// Authorization and transfer checks (Asset-based dispatchers) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] TER +requireAuth( + ReadView const& view, + Asset const& asset, + AccountID const& account, + AuthType authType = AuthType::Legacy); + +[[nodiscard]] TER +canTransfer(ReadView const& view, Asset const& asset, AccountID const& from, AccountID const& to); + +//------------------------------------------------------------------------------ +// +// Money Transfers (Asset-based dispatchers) +// +//------------------------------------------------------------------------------ + +// Direct send w/o fees: +// - Redeeming IOUs and/or sending sender's own IOUs. +// - Create trust line of needed. +// --> bCheckIssuer : normally require issuer to be involved. +// [[nodiscard]] // nodiscard commented out so DirectStep.cpp compiles. + +/** Calls static directSendNoFeeIOU if saAmount represents Issue. + * Calls static directSendNoFeeMPT if saAmount represents MPTIssue. + */ +TER +directSendNoFee( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + bool bCheckIssuer, + beast::Journal j); + +/** Calls static accountSendIOU if saAmount represents Issue. + * Calls static accountSendMPT if saAmount represents MPTIssue. + */ +[[nodiscard]] TER +accountSend( + ApplyView& view, + AccountID const& from, + AccountID const& to, + STAmount const& saAmount, + beast::Journal j, + WaiveTransferFee waiveFee = WaiveTransferFee::No); + +using MultiplePaymentDestinations = std::vector>; +/** Like accountSend, except one account is sending multiple payments (with the + * same asset!) simultaneously + * + * Calls static accountSendMultiIOU if saAmount represents Issue. + * Calls static accountSendMultiMPT if saAmount represents MPTIssue. + */ +[[nodiscard]] TER +accountSendMulti( + ApplyView& view, + AccountID const& senderID, + Asset const& asset, + MultiplePaymentDestinations const& receivers, + beast::Journal j, + WaiveTransferFee waiveFee = WaiveTransferFee::No); + +[[nodiscard]] TER +transferXRP( + ApplyView& view, + AccountID const& from, + AccountID const& to, + STAmount const& amount, + beast::Journal j); + +} // namespace xrpl diff --git a/include/xrpl/ledger/helpers/VaultHelpers.h b/include/xrpl/ledger/helpers/VaultHelpers.h new file mode 100644 index 0000000000..8aef30aa27 --- /dev/null +++ b/include/xrpl/ledger/helpers/VaultHelpers.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include + +#include +#include + +namespace xrpl { + +/** From the perspective of a vault, return the number of shares to give + depositor when they offer a fixed amount of assets. Note, since shares are + MPT, this number is integral and always truncated in this calculation. + + @param vault The vault SLE. + @param issuance The MPTokenIssuance SLE for the vault's shares. + @param assets The amount of assets to convert. + + @return The number of shares, or nullopt on error. +*/ +[[nodiscard]] std::optional +assetsToSharesDeposit( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& assets); + +/** From the perspective of a vault, return the number of assets to take from + depositor when they receive a fixed amount of shares. Note, since shares are + MPT, they are always an integral number. + + @param vault The vault SLE. + @param issuance The MPTokenIssuance SLE for the vault's shares. + @param shares The amount of shares to convert. + + @return The number of assets, or nullopt on error. +*/ +[[nodiscard]] std::optional +sharesToAssetsDeposit( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& shares); + +/** Controls whether to truncate shares instead of rounding. */ +enum class TruncateShares : bool { no = false, yes = true }; + +/** From the perspective of a vault, return the number of shares to demand from + the depositor when they ask to withdraw a fixed amount of assets. Since + shares are MPT this number is integral, and it will be rounded to nearest + unless explicitly requested to be truncated instead. + + @param vault The vault SLE. + @param issuance The MPTokenIssuance SLE for the vault's shares. + @param assets The amount of assets to convert. + @param truncate Whether to truncate instead of rounding. + + @return The number of shares, or nullopt on error. +*/ +[[nodiscard]] std::optional +assetsToSharesWithdraw( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& assets, + TruncateShares truncate = TruncateShares::no); + +/** From the perspective of a vault, return the number of assets to give the + depositor when they redeem a fixed amount of shares. Note, since shares are + MPT, they are always an integral number. + + @param vault The vault SLE. + @param issuance The MPTokenIssuance SLE for the vault's shares. + @param shares The amount of shares to convert. + + @return The number of assets, or nullopt on error. +*/ +[[nodiscard]] std::optional +sharesToAssetsWithdraw( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& shares); + +} // namespace xrpl diff --git a/include/xrpl/net/HTTPClient.h b/include/xrpl/net/HTTPClient.h index 9ea5f98a37..c176e2d2e8 100644 --- a/include/xrpl/net/HTTPClient.h +++ b/include/xrpl/net/HTTPClient.h @@ -29,6 +29,18 @@ public: bool sslVerify, beast::Journal j); + /** Destroys the global SSL context created by initializeSSLContext(). + * + * This releases the underlying boost::asio::ssl::context and any + * associated OpenSSL resources. Must not be called while any + * HTTPClient requests are in flight. + * + * @note Currently only called from tests during teardown. In production, + * the SSL context lives for the lifetime of the process. + */ + static void + cleanupSSLContext(); + static void get(bool bSSL, boost::asio::io_context& io_context, diff --git a/include/xrpl/nodestore/README.md b/include/xrpl/nodestore/README.md index 4b228bfc9a..b78c99e27d 100644 --- a/include/xrpl/nodestore/README.md +++ b/include/xrpl/nodestore/README.md @@ -51,7 +51,7 @@ A blob containing the payload. Stored in the following format. --- The `NodeStore` provides an interface that stores, in a persistent database, a -collection of NodeObjects that rippled uses as its primary representation of +collection of NodeObjects that xrpld uses as its primary representation of ledger entries. All ledger entries are stored as NodeObjects and as such, need to be persisted between launches. If a NodeObject is accessed and is not in memory, it will be retrieved from the database. @@ -110,7 +110,7 @@ The `NodeStore.Timing` test is used to execute a set of read/write workloads to compare current available nodestore backends. It can be executed with: ``` -$rippled --unittest=NodeStoreTiming +$xrpld --unittest=NodeStoreTiming ``` It is also possible to use alternate DB config params by passing config strings @@ -143,10 +143,10 @@ Through various executions and profiling some conclusions are presented below. just after ledger close, then that would provide similar, but more predictable guarantees. It would also remove an unneeded thread and unnecessary memory usage. An alternative point of view is that because there will always be many - other rippled instances running there is no need for such guarantees. The nodes + other xrpld instances running there is no need for such guarantees. The nodes will always be available from another peer. -- Lookup in a block was previously using binary search. With rippled's use case +- Lookup in a block was previously using binary search. With xrpld's use case it is highly unlikely that two adjacent key/values will ever be requested one after the other. Therefore hash indexing of blocks makes much more sense. Rocksdb has a number of options for hash indexing both memtables and blocks and diff --git a/include/xrpl/nodestore/Scheduler.h b/include/xrpl/nodestore/Scheduler.h index 6e01533930..dc3e1e3d15 100644 --- a/include/xrpl/nodestore/Scheduler.h +++ b/include/xrpl/nodestore/Scheduler.h @@ -16,7 +16,7 @@ struct FetchReport { } - std::chrono::milliseconds elapsed; + std::chrono::milliseconds elapsed{}; FetchType const fetchType; bool wasFound = false; }; diff --git a/include/xrpl/nodestore/detail/BatchWriter.h b/include/xrpl/nodestore/detail/BatchWriter.h index 1820435133..93993d1c3e 100644 --- a/include/xrpl/nodestore/detail/BatchWriter.h +++ b/include/xrpl/nodestore/detail/BatchWriter.h @@ -71,8 +71,8 @@ private: Scheduler& m_scheduler; LockType mWriteMutex; CondvarType mWriteCondition; - int mWriteLoad; - bool mWritePending; + int mWriteLoad{0}; + bool mWritePending{false}; Batch mWriteSet; }; diff --git a/include/xrpl/nodestore/detail/EncodedBlob.h b/include/xrpl/nodestore/detail/EncodedBlob.h index 78e7153f73..b05583475e 100644 --- a/include/xrpl/nodestore/detail/EncodedBlob.h +++ b/include/xrpl/nodestore/detail/EncodedBlob.h @@ -35,7 +35,7 @@ namespace NodeStore { class EncodedBlob { /** The 32-byte key of the serialized object. */ - std::array key_; + std::array key_{}; /** A pre-allocated buffer for the serialized object. @@ -43,7 +43,8 @@ class EncodedBlob 1024 more bytes. The precise size is calculated automatically at compile time so as to avoid wasting space on padding bytes. */ - std::array payload_; + std::array + payload_{}; /** The size of the serialized data. */ std::uint32_t size_; diff --git a/include/xrpl/nodestore/detail/codec.h b/include/xrpl/nodestore/detail/codec.h index 8a337ca4c3..4e12c6b4db 100644 --- a/include/xrpl/nodestore/detail/codec.h +++ b/include/xrpl/nodestore/detail/codec.h @@ -56,7 +56,7 @@ lz4_compress(void const* in, std::size_t in_size, BufferFactory&& bf) using std::runtime_error; using namespace nudb::detail; std::pair result; - std::array::max> vi; + std::array::max> vi{}; auto const n = write_varint(vi.data(), in_size); auto const out_max = LZ4_compressBound(in_size); std::uint8_t* out = reinterpret_cast(bf(n + out_max)); @@ -88,7 +88,7 @@ nodeobject_decompress(void const* in, std::size_t in_size, BufferFactory&& bf) using namespace nudb::detail; std::uint8_t const* p = reinterpret_cast(in); - std::size_t type; + std::size_t type = 0; auto const vn = read_varint(p, in_size, type); if (vn == 0) Throw("nodeobject decompress"); @@ -117,7 +117,7 @@ nodeobject_decompress(void const* in, std::size_t in_size, BufferFactory&& bf) "nodeobject codec v1: short inner node size: " + std::string("in_size = ") + std::to_string(in_size) + " hs = " + std::to_string(hs)); istream is(p, in_size); - std::uint16_t mask; + std::uint16_t mask = 0; read(is, mask); // Mask in_size -= hs; result.second = 525; @@ -196,10 +196,10 @@ nodeobject_compress(void const* in, std::size_t in_size, BufferFactory&& bf) if (in_size == 525) { istream is(in, in_size); - std::uint32_t index; - std::uint32_t unused; - std::uint8_t kind; - std::uint32_t prefix; + std::uint32_t index = 0; + std::uint32_t unused = 0; + std::uint8_t kind = 0; + std::uint32_t prefix = 0; read(is, index); read(is, unused); read(is, kind); @@ -208,7 +208,7 @@ nodeobject_compress(void const* in, std::size_t in_size, BufferFactory&& bf) { std::size_t n = 0; std::uint16_t mask = 0; - std::array vh; + std::array vh{}; for (unsigned bit = 0x8000; bit; bit >>= 1) { void const* const h = is(32); @@ -247,7 +247,7 @@ nodeobject_compress(void const* in, std::size_t in_size, BufferFactory&& bf) } } - std::array::max> vi; + std::array::max> vi{}; constexpr std::size_t codecType = 1; auto const vn = write_varint(vi.data(), codecType); @@ -257,7 +257,7 @@ nodeobject_compress(void const* in, std::size_t in_size, BufferFactory&& bf) // case 0 was uncompressed data; we always compress now. case 1: // lz4 { - std::uint8_t* p; + std::uint8_t* p = nullptr; auto const lzr = NodeStore::lz4_compress(in, in_size, [&p, &vn, &bf](std::size_t n) { p = reinterpret_cast(bf(vn + n)); return p + vn; @@ -287,10 +287,10 @@ filter_inner(void* in, std::size_t in_size) if (in_size == 525) { istream is(in, in_size); - std::uint32_t index; - std::uint32_t unused; - std::uint8_t kind; - std::uint32_t prefix; + std::uint32_t index = 0; + std::uint32_t unused = 0; + std::uint8_t kind = 0; + std::uint32_t prefix = 0; read(is, index); read(is, unused); read(is, kind); diff --git a/include/xrpl/nodestore/detail/varint.h b/include/xrpl/nodestore/detail/varint.h index 98f8d8ff08..c84ed383ed 100644 --- a/include/xrpl/nodestore/detail/varint.h +++ b/include/xrpl/nodestore/detail/varint.h @@ -82,6 +82,7 @@ template std::size_t write_varint(void* p0, std::size_t v) { + // NOLINTNEXTLINE(misc-const-correctness) std::uint8_t* p = reinterpret_cast(p0); do { diff --git a/include/xrpl/proto/org/xrpl/rpc/v1/README.md b/include/xrpl/proto/org/xrpl/rpc/v1/README.md index e9b9b55841..e8566ec179 100644 --- a/include/xrpl/proto/org/xrpl/rpc/v1/README.md +++ b/include/xrpl/proto/org/xrpl/rpc/v1/README.md @@ -1,8 +1,8 @@ # Protocol buffer definitions for gRPC -This folder contains the protocol buffer definitions used by the rippled gRPC API. +This folder contains the protocol buffer definitions used by the xrpld gRPC API. The gRPC API attempts to mimic the JSON/Websocket API as much as possible. -As of April 2020, the gRPC API supports a subset of the full rippled API: +As of April 2020, the gRPC API supports a subset of the full xrpld API: tx, account_tx, account_info, fee and submit. ### Making Changes @@ -63,7 +63,7 @@ templated `CallData` class in GRPCServerImpl::setupListeners(). The template parameters should be the request type and the response type. Finally, define the handler itself in the appropriate file under the -src/ripple/rpc/handlers folder. If the method already has a JSON/Websocket +src/xrpld/rpc/handlers folder. If the method already has a JSON/Websocket equivalent, write the gRPC handler in the same file, and abstract common logic into helper functions (see Tx.cpp or AccountTx.cpp for an example). diff --git a/include/xrpl/proto/xrpl.proto b/include/xrpl/proto/xrpl.proto index 0af7deb35d..cd82ed24e6 100644 --- a/include/xrpl/proto/xrpl.proto +++ b/include/xrpl/proto/xrpl.proto @@ -36,7 +36,7 @@ enum MessageType { /* Provides the current ephemeral key for a validator. */ message TMManifest { - // A Manifest object in the Ripple serialization format. + // A Manifest object in the XRPL serialization format. required bytes stobject = 1; } diff --git a/include/xrpl/protocol/AccountID.h b/include/xrpl/protocol/AccountID.h index bc57058d9e..641f2f15c1 100644 --- a/include/xrpl/protocol/AccountID.h +++ b/include/xrpl/protocol/AccountID.h @@ -2,7 +2,7 @@ #include // VFALCO Uncomment when the header issues are resolved -// #include +// #include #include #include #include diff --git a/include/xrpl/protocol/AmountConversions.h b/include/xrpl/protocol/AmountConversions.h index e1a224dac9..7e41eff41a 100644 --- a/include/xrpl/protocol/AmountConversions.h +++ b/include/xrpl/protocol/AmountConversions.h @@ -102,7 +102,7 @@ template T toAmount(Issue const& issue, Number const& n, Number::rounding_mode mode = Number::getround()) { - saveNumberRoundMode rm(Number::getround()); + saveNumberRoundMode const rm(Number::getround()); if (isXRP(issue)) Number::setround(mode); diff --git a/include/xrpl/protocol/BuildInfo.h b/include/xrpl/protocol/BuildInfo.h index cc7633cfe1..522747bc94 100644 --- a/include/xrpl/protocol/BuildInfo.h +++ b/include/xrpl/protocol/BuildInfo.h @@ -33,7 +33,7 @@ getFullVersionString(); X: 16 bits identifying the particular implementation Y: 48 bits of data specific to the implementation - The rippled-specific format (implementation ID is: 0x18 0x3B) is: + The xrpld-specific format (implementation ID is: 0x18 0x3B) is: 00011000-00111011-MMMMMMMM-mmmmmmmm-pppppppp-TTNNNNNN-00000000-00000000 @@ -55,23 +55,23 @@ encodeSoftwareVersion(std::string_view versionStr); std::uint64_t getEncodedVersion(); -/** Check if the encoded software version is a rippled software version. +/** Check if the encoded software version is an xrpld software version. @param version another node's encoded software version - @return true if the version is a rippled software version, false otherwise + @return true if the version is an xrpld software version, false otherwise */ bool -isRippledVersion(std::uint64_t version); +isXrpldVersion(std::uint64_t version); -/** Check if the version is newer than the local node's rippled software +/** Check if the version is newer than the local node's xrpld software version. @param version another node's encoded software version - @return true if the version is newer than the local node's rippled software + @return true if the version is newer than the local node's xrpld software version, false otherwise. @note This function only understands version numbers that are generated by - rippled. Please see the encodeSoftwareVersion() function for detail. + xrpld. Please see the encodeSoftwareVersion() function for detail. */ bool isNewerVersion(std::uint64_t version); diff --git a/include/xrpl/protocol/ErrorCodes.h b/include/xrpl/protocol/ErrorCodes.h index c138d5d39a..2f16415bdf 100644 --- a/include/xrpl/protocol/ErrorCodes.h +++ b/include/xrpl/protocol/ErrorCodes.h @@ -153,7 +153,7 @@ enum warning_code_i { warnRPC_AMENDMENT_BLOCKED = 1002, warnRPC_EXPIRED_VALIDATOR_LIST = 1003, // unused = 1004 - warnRPC_FIELDS_DEPRECATED = 2004, // rippled needs to maintain + warnRPC_FIELDS_DEPRECATED = 2004, // xrpld needs to maintain // compatibility with Clio on this code. }; diff --git a/include/xrpl/protocol/Feature.h b/include/xrpl/protocol/Feature.h index 34d78fc50b..112f66f4a1 100644 --- a/include/xrpl/protocol/Feature.h +++ b/include/xrpl/protocol/Feature.h @@ -37,10 +37,10 @@ * 5) If a supported feature (`Supported::yes`) was _ever_ in a released * version, it can never be changed back to `Supported::no`, because * it _may_ still become enabled at any time. This would cause newer - * versions of `rippled` to become amendment blocked. + * versions of `xrpld` to become amendment blocked. * Instead, to prevent newer versions from voting on the feature, use * `VoteBehavior::Obsolete`. Obsolete features can not be voted for - * by any versions of `rippled` built with that setting, but will still + * by any versions of `xrpld` built with that setting, but will still * work correctly if they get enabled. If a feature remains obsolete * for long enough that _all_ clients that could vote for it are * amendment blocked, the feature can be removed from the code @@ -64,6 +64,49 @@ namespace xrpl { +// Feature names must not exceed this length (in characters, excluding the null terminator). +static constexpr std::size_t maxFeatureNameSize = 63; +// Reserve this exact feature-name length (in characters/bytes, excluding the null terminator) +// so that a 32-byte uint256 (for example, in WASM or other interop contexts) can be used +// as a compact, fixed-size feature selector without conflicting with human-readable names. +static constexpr std::size_t reservedFeatureNameSize = 32; + +// Both validFeatureNameSize and validFeatureName are consteval functions that can be used in +// static_asserts to validate feature names at compile time. They are only used inside +// enforceValidFeatureName in Feature.cpp, but are exposed here for testing. The expected +// parameter `auto fn` is a constexpr lambda which returns a const char*, making it available +// for compile-time evaluation. Read more in https://accu.org/journals/overload/30/172/wu/ +consteval auto +validFeatureNameSize(auto fn) -> bool +{ + constexpr char const* n = fn(); + // Note, std::strlen is not constexpr, we need to implement our own here. + constexpr std::size_t N = [](auto n) { + std::size_t ret = 0; + for (auto ptr = n; *ptr != '\0'; ret++, ++ptr) + ; + return ret; + }(n); + return N != reservedFeatureNameSize && // + N <= maxFeatureNameSize; +} + +consteval auto +validFeatureName(auto fn) -> bool +{ + constexpr char const* n = fn(); + // Prevent the use of visually confusable characters and enforce that feature names + // are always valid ASCII. This is needed because C++ allows Unicode identifiers. + // Characters below 0x20 are nonprintable control characters, and characters with the 0x80 bit + // set are non-ASCII (e.g. UTF-8 encoding of Unicode), so both are disallowed. + for (auto ptr = n; *ptr != '\0'; ++ptr) + { + if (*ptr & 0x80 || *ptr < 0x20) + return false; + } + return true; +} + enum class VoteBehavior : int { Obsolete = -1, DefaultNo = 0, DefaultYes }; enum class AmendmentSupport : int { Retired = -1, Supported = 0, Unsupported }; diff --git a/include/xrpl/protocol/Fees.h b/include/xrpl/protocol/Fees.h index 84c8ffe81f..ddf4acbf67 100644 --- a/include/xrpl/protocol/Fees.h +++ b/include/xrpl/protocol/Fees.h @@ -4,6 +4,10 @@ namespace xrpl { +// Deprecated constant for backwards compatibility with pre-XRPFees amendment. +// This was the reference fee units used in the old fee calculation. +inline constexpr std::uint32_t FEE_UNITS_DEPRECATED = 10; + /** Reflects the fee settings for a particular ledger. The fees are always the same for any transactions applied @@ -11,15 +15,25 @@ namespace xrpl { */ struct Fees { - XRPAmount base{0}; // Reference tx cost (drops) - XRPAmount reserve{0}; // Reserve base (drops) - XRPAmount increment{0}; // Reserve increment (drops) + /** @brief Cost of a reference transaction in drops. */ + XRPAmount base{0}; + + /** @brief Minimum XRP an account must hold to exist on the ledger. */ + XRPAmount reserve{0}; + + /** @brief Additional XRP reserve required per owned ledger object. */ + XRPAmount increment{0}; explicit Fees() = default; Fees(Fees const&) = default; Fees& operator=(Fees const&) = default; + Fees(XRPAmount base_, XRPAmount reserve_, XRPAmount increment_) + : base(base_), reserve(reserve_), increment(increment_) + { + } + /** Returns the account reserve given the owner count, in drops. The reserve is calculated as the reserve base plus diff --git a/include/xrpl/protocol/IOUAmount.h b/include/xrpl/protocol/IOUAmount.h index 2ee453f575..47aa35e0e8 100644 --- a/include/xrpl/protocol/IOUAmount.h +++ b/include/xrpl/protocol/IOUAmount.h @@ -26,8 +26,8 @@ class IOUAmount : private boost::totally_ordered, private boost::addi private: using mantissa_type = std::int64_t; using exponent_type = int; - mantissa_type mantissa_; - exponent_type exponent_; + mantissa_type mantissa_{}; + exponent_type exponent_{}; /** Adjusts the mantissa and exponent to the proper range. diff --git a/include/xrpl/protocol/Indexes.h b/include/xrpl/protocol/Indexes.h index 7884905d9e..574bbfbde6 100644 --- a/include/xrpl/protocol/Indexes.h +++ b/include/xrpl/protocol/Indexes.h @@ -363,11 +363,12 @@ uint256 getTicketIndex(AccountID const& account, SeqProxy ticketSeq); template +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct keyletDesc { std::function function; Json::StaticString expectedLEName; - bool includeInTests; + bool includeInTests{}; }; // This list should include all of the keylet functions that take a single diff --git a/include/xrpl/protocol/Issue.h b/include/xrpl/protocol/Issue.h index c8e598a540..7bd01185c9 100644 --- a/include/xrpl/protocol/Issue.h +++ b/include/xrpl/protocol/Issue.h @@ -96,7 +96,7 @@ operator<=>(Issue const& lhs, Issue const& rhs) inline Issue const& xrpIssue() { - static Issue issue{xrpCurrency(), xrpAccount()}; + static Issue const issue{xrpCurrency(), xrpAccount()}; return issue; } @@ -104,7 +104,7 @@ xrpIssue() inline Issue const& noIssue() { - static Issue issue{noCurrency(), noAccount()}; + static Issue const issue{noCurrency(), noAccount()}; return issue; } diff --git a/include/xrpl/protocol/LedgerHeader.h b/include/xrpl/protocol/LedgerHeader.h index 1dc67cada5..6e22ad268d 100644 --- a/include/xrpl/protocol/LedgerHeader.h +++ b/include/xrpl/protocol/LedgerHeader.h @@ -72,4 +72,8 @@ deserializeHeader(Slice data, bool hasHash = false); LedgerHeader deserializePrefixedHeader(Slice data, bool hasHash = false); +/** Calculate the hash of a ledger header. */ +uint256 +calculateLedgerHash(LedgerHeader const& info); + } // namespace xrpl diff --git a/include/xrpl/protocol/Permissions.h b/include/xrpl/protocol/Permissions.h index e8800a41d2..0ec4f04f1a 100644 --- a/include/xrpl/protocol/Permissions.h +++ b/include/xrpl/protocol/Permissions.h @@ -65,19 +65,19 @@ public: std::optional getGranularTxType(GranularPermissionType const& gpType) const; - std::optional> const + std::optional> getTxFeature(TxType txType) const; bool isDelegable(std::uint32_t const& permissionValue, Rules const& rules) const; // for tx level permission, permission value is equal to tx type plus one - uint32_t - txToPermissionType(TxType const& type) const; + static uint32_t + txToPermissionType(TxType const& type); // tx type value is permission value minus one - TxType - permissionToTxType(uint32_t const& value) const; + static TxType + permissionToTxType(uint32_t const& value); }; } // namespace xrpl diff --git a/include/xrpl/protocol/Protocol.h b/include/xrpl/protocol/Protocol.h index 7ddf43d175..0db7b217f0 100644 --- a/include/xrpl/protocol/Protocol.h +++ b/include/xrpl/protocol/Protocol.h @@ -209,7 +209,7 @@ std::size_t constexpr maxDIDDocumentLength = 256; std::size_t constexpr maxDIDURILength = 256; /** The maximum length of an Attestation inside a DID */ -std::size_t constexpr maxDIDAttestationLength = 256; +std::size_t constexpr maxDIDDataLength = 256; /** The maximum length of a domain */ std::size_t constexpr maxDomainLength = 256; diff --git a/include/xrpl/protocol/PublicKey.h b/include/xrpl/protocol/PublicKey.h index e806f6ffeb..64ced93ffb 100644 --- a/include/xrpl/protocol/PublicKey.h +++ b/include/xrpl/protocol/PublicKey.h @@ -21,7 +21,7 @@ namespace xrpl { Public keys are used in the public-key cryptography system used to verify signatures attached to messages. - The format of the public key is Ripple specific, + The format of the public key is XRPL specific, information needed to determine the cryptosystem parameters used is stored inside the key. @@ -44,7 +44,7 @@ protected: // All the constructed public keys are valid, non-empty and contain 33 // bytes of data. static constexpr std::size_t size_ = 33; - std::uint8_t buf_[size_]; // should be large enough + std::uint8_t buf_[size_]{}; // should be large enough public: using const_iterator = std::uint8_t const*; diff --git a/include/xrpl/protocol/Quality.h b/include/xrpl/protocol/Quality.h index 09b0f4e7cb..851e34d396 100644 --- a/include/xrpl/protocol/Quality.h +++ b/include/xrpl/protocol/Quality.h @@ -56,8 +56,8 @@ struct TAmounts return *this; } - In in; - Out out; + In in{}; + Out out{}; }; using Amounts = TAmounts; @@ -78,7 +78,7 @@ operator!=(TAmounts const& lhs, TAmounts const& rhs) noexcept //------------------------------------------------------------------------------ -// Ripple specific constant used for parsing qualities and other things +// XRPL specific constant used for parsing qualities and other things #define QUALITY_ONE 1'000'000'000 /** Represents the logical ratio of output currency to input currency. @@ -304,8 +304,8 @@ Quality::ceil_TAmounts_helper( // Use the existing STAmount implementation for now, but consider // replacing with code specific to IOUAMount and XRPAmount - Amounts stAmt(toSTAmount(amount.in), toSTAmount(amount.out)); - STAmount stLim(toSTAmount(limit)); + Amounts const stAmt(toSTAmount(amount.in), toSTAmount(amount.out)); + STAmount const stLim(toSTAmount(limit)); Amounts const stRes = ((*this).*ceil_function)(stAmt, stLim, roundUp...); return TAmounts(toAmount(stRes.in), toAmount(stRes.out)); } diff --git a/include/xrpl/protocol/README.md b/include/xrpl/protocol/README.md index a6e8d24982..d679c583d4 100644 --- a/include/xrpl/protocol/README.md +++ b/include/xrpl/protocol/README.md @@ -22,7 +22,7 @@ optional fields easier to read: - The operation `x[~sfFoo]` means "return the value of 'Foo' if it exists, or nothing if it doesn't." This usage of the tilde/bitwise NOT operator is not standard outside of the - `rippled` codebase. + `xrpld` codebase. - As a consequence of this, `x[~sfFoo] = y[~sfFoo]` assigns the value of Foo from y to x, including omitting Foo from x if it doesn't exist in y. @@ -33,7 +33,7 @@ or may not hold a value. For things not guaranteed to exist, you use `x[~sfFoo]` because you want such a container. It avoids having to look something up twice, once just to see if it exists and a second time to get/set its value. -([Real example](https://github.com/ripple/rippled/blob/35f4698aed5dce02f771b34cfbb690495cb5efcc/src/ripple/app/tx/impl/PayChan.cpp#L229-L236)) +([Real example](https://github.com/XRPLF/rippled/blob/35f4698aed5dce02f771b34cfbb690495cb5efcc/src/ripple/app/tx/impl/PayChan.cpp#L229-L236)) The source of this "type magic" is in [SField.h](./SField.h#L296-L302). diff --git a/include/xrpl/protocol/RippleLedgerHash.h b/include/xrpl/protocol/RippleLedgerHash.h index 2a51298d3d..9dab644663 100644 --- a/include/xrpl/protocol/RippleLedgerHash.h +++ b/include/xrpl/protocol/RippleLedgerHash.h @@ -6,4 +6,4 @@ namespace xrpl { using LedgerHash = uint256; -} +} // namespace xrpl diff --git a/include/xrpl/protocol/STAccount.h b/include/xrpl/protocol/STAccount.h index 3fdb2e015e..b1f112fbb2 100644 --- a/include/xrpl/protocol/STAccount.h +++ b/include/xrpl/protocol/STAccount.h @@ -13,7 +13,7 @@ class STAccount final : public STBase, public CountedObject private: // The original implementation of STAccount kept the value in an STBlob. // But an STAccount is always 160 bits, so we can store it with less - // overhead in a xrpl::uint160. However, so the serialized format of the + // overhead in an xrpl::uint160. However, so the serialized format of the // STAccount stays unchanged, we serialize and deserialize like an STBlob. AccountID value_; bool default_; @@ -24,7 +24,7 @@ public: STAccount(); STAccount(SField const& n); - STAccount(SField const& n, Buffer&& v); + STAccount(SField const& n, Buffer const& v); STAccount(SerialIter& sit, SField const& name); STAccount(SField const& n, AccountID const& v); diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index dadeec096f..df26f99b75 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -35,9 +35,9 @@ public: private: Asset mAsset; - mantissa_type mValue; + mantissa_type mValue{}; exponent_type mOffset; - bool mIsNegative; + bool mIsNegative{}; public: using value_type = STAmount; @@ -532,7 +532,7 @@ STAmount::fromNumber(A const& a, Number const& number) { bool const negative = number.mantissa() < 0; Number const working{negative ? -number : number}; - Asset asset{a}; + Asset const asset{a}; if (asset.integral()) { std::uint64_t const intValue = static_cast(working); @@ -716,7 +716,7 @@ roundToAsset( std::int32_t scale, Number::rounding_mode rounding = Number::getround()) { - NumberRoundModeGuard mg(rounding); + NumberRoundModeGuard const mg(rounding); STAmount const ret{asset, value}; if (ret.integral()) return ret; diff --git a/include/xrpl/protocol/STBase.h b/include/xrpl/protocol/STBase.h index 7f06b01ca4..8edeb26424 100644 --- a/include/xrpl/protocol/STBase.h +++ b/include/xrpl/protocol/STBase.h @@ -83,7 +83,7 @@ to_json(T const& t) namespace detail { class STVar; -} +} // namespace detail // VFALCO TODO fix this restriction on copy assignment. // diff --git a/include/xrpl/protocol/STLedgerEntry.h b/include/xrpl/protocol/STLedgerEntry.h index dde7d49ebd..a28868cd7a 100644 --- a/include/xrpl/protocol/STLedgerEntry.h +++ b/include/xrpl/protocol/STLedgerEntry.h @@ -8,7 +8,7 @@ namespace xrpl { class Rules; namespace test { class Invariants_test; -} +} // namespace test class STLedgerEntry final : public STObject, public CountedObject { @@ -86,7 +86,9 @@ inline STLedgerEntry::STLedgerEntry(LedgerEntryType type, uint256 const& key) { } -inline STLedgerEntry::STLedgerEntry(SerialIter&& sit, uint256 const& index) +inline STLedgerEntry::STLedgerEntry( + SerialIter&& sit, // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) + uint256 const& index) : STLedgerEntry(sit, index) { } diff --git a/include/xrpl/protocol/STObject.h b/include/xrpl/protocol/STObject.h index 09eff8c3d2..61a1cce05e 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -57,7 +57,7 @@ class STObject : public STBase, public CountedObject using list_type = std::vector; list_type v_; - SOTemplate const* mType; + SOTemplate const* mType{}; public: using iterator = boost::transform_iterator; @@ -401,7 +401,7 @@ public: getStyle(SField const& field) const; bool - hasMatchingEntry(STBase const&); + hasMatchingEntry(STBase const&) const; bool operator==(STObject const& o) const; @@ -671,7 +671,7 @@ public: OptionalProxy& operator=(std::nullopt_t const&); OptionalProxy& - operator=(optional_type&& v); + operator=(optional_type&& v); // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) OptionalProxy& operator=(optional_type const& v); @@ -766,7 +766,7 @@ STObject::Proxy::assign(U&& u) st_->makeFieldAbsent(*f_); return; } - T* t; + T* t = nullptr; if (style_ == soeINVALID) t = dynamic_cast(st_->getPField(*f_, true)); else @@ -851,7 +851,9 @@ STObject::OptionalProxy::operator=(std::nullopt_t const&) -> OptionalProxy& template auto -STObject::OptionalProxy::operator=(optional_type&& v) -> OptionalProxy& +STObject::OptionalProxy::operator=( + optional_type&& v) // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) + -> OptionalProxy& { if (v) this->assign(std::move(*v)); @@ -930,6 +932,7 @@ STObject::Transform::operator()(detail::STVar const& e) const //------------------------------------------------------------------------------ +// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) inline STObject::STObject(SerialIter&& sit, SField const& name) : STObject(sit, name) { } @@ -1153,7 +1156,7 @@ STObject::getFieldByValue(SField const& field) const if (!rf) throwFieldNotFound(field); - SerializedTypeID id = rf->getSType(); + SerializedTypeID const id = rf->getSType(); if (id == STI_NOTPRESENT) return V(); // optional field not present @@ -1180,7 +1183,7 @@ STObject::getFieldByConstRef(SField const& field, V const& empty) const if (!rf) throwFieldNotFound(field); - SerializedTypeID id = rf->getSType(); + SerializedTypeID const id = rf->getSType(); if (id == STI_NOTPRESENT) return empty; // optional field not present diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index 72bd38d5d6..b27d9e0637 100644 --- a/include/xrpl/protocol/STTx.h +++ b/include/xrpl/protocol/STTx.h @@ -15,7 +15,7 @@ namespace xrpl { -enum TxnSql : char { +enum class TxnSql : char { txnSqlNew = 'N', txnSqlConflict = 'C', txnSqlHeld = 'H', @@ -83,6 +83,9 @@ public: std::uint32_t getSeqValue() const; + AccountID + getFeePayer() const; + boost::container::flat_set getMentionedAccounts() const; @@ -122,7 +125,7 @@ public: getMetaSQL( Serializer rawTxn, std::uint32_t inLedger, - char status, + TxnSql status, std::string const& escapedMetaData) const; std::vector const& @@ -176,7 +179,8 @@ sterilize(STTx const& stx); bool isPseudoTx(STObject const& tx); -inline STTx::STTx(SerialIter&& sit) : STTx(sit) +inline STTx::STTx(SerialIter&& sit) // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) + : STTx(sit) { } diff --git a/include/xrpl/protocol/STVector256.h b/include/xrpl/protocol/STVector256.h index dd77067b7c..815224def2 100644 --- a/include/xrpl/protocol/STVector256.h +++ b/include/xrpl/protocol/STVector256.h @@ -69,9 +69,6 @@ public: std::vector::iterator insert(std::vector::const_iterator pos, uint256 const& value); - std::vector::iterator - insert(std::vector::const_iterator pos, uint256&& value); - void push_back(uint256 const& v); @@ -184,12 +181,6 @@ STVector256::insert(std::vector::const_iterator pos, uint256 const& val return mValue.insert(pos, value); } -inline std::vector::iterator -STVector256::insert(std::vector::const_iterator pos, uint256&& value) -{ - return mValue.insert(pos, std::move(value)); -} - inline void STVector256::push_back(uint256 const& v) { diff --git a/include/xrpl/protocol/SecretKey.h b/include/xrpl/protocol/SecretKey.h index a59b1150c1..530b6fc35f 100644 --- a/include/xrpl/protocol/SecretKey.h +++ b/include/xrpl/protocol/SecretKey.h @@ -16,8 +16,11 @@ namespace xrpl { /** A secret key. */ class SecretKey { +public: + static constexpr std::size_t size_ = 32; + private: - std::uint8_t buf_[32]; + std::uint8_t buf_[size_]{}; public: using const_iterator = std::uint8_t const*; @@ -27,9 +30,14 @@ public: SecretKey& operator=(SecretKey const&) = default; + bool + operator==(SecretKey const&) = delete; + bool + operator!=(SecretKey const&) = delete; + ~SecretKey(); - SecretKey(std::array const& data); + SecretKey(std::array const& data); SecretKey(Slice const& slice); std::uint8_t const* @@ -78,16 +86,10 @@ public: }; inline bool -operator==(SecretKey const& lhs, SecretKey const& rhs) -{ - return lhs.size() == rhs.size() && std::memcmp(lhs.data(), rhs.data(), rhs.size()) == 0; -} +operator==(SecretKey const& lhs, SecretKey const& rhs) = delete; inline bool -operator!=(SecretKey const& lhs, SecretKey const& rhs) -{ - return !(lhs == rhs); -} +operator!=(SecretKey const& lhs, SecretKey const& rhs) = delete; //------------------------------------------------------------------------------ @@ -116,7 +118,7 @@ derivePublicKey(KeyType type, SecretKey const& sk); /** Generate a key pair deterministically. - This algorithm is specific to Ripple: + This algorithm is specific to the XRPL: For secp256k1 key pairs, the seed is converted to a Generator and used to compute the key pair diff --git a/include/xrpl/protocol/Seed.h b/include/xrpl/protocol/Seed.h index 1fad94e2bd..04e8481c8f 100644 --- a/include/xrpl/protocol/Seed.h +++ b/include/xrpl/protocol/Seed.h @@ -13,7 +13,7 @@ namespace xrpl { class Seed { private: - std::array buf_; + std::array buf_{}; public: using const_iterator = std::array::const_iterator; @@ -80,7 +80,7 @@ randomSeed(); /** Generate a seed deterministically. - The algorithm is specific to Ripple: + The algorithm is specific to the XRPL: The seed is calculated as the first 128 bits of the SHA512-Half of the string text excluding diff --git a/include/xrpl/protocol/Serializer.h b/include/xrpl/protocol/Serializer.h index 98cefb4d08..2d3489fe7b 100644 --- a/include/xrpl/protocol/Serializer.h +++ b/include/xrpl/protocol/Serializer.h @@ -69,7 +69,7 @@ public: int add32(T i) { - int ret = mData.size(); + int const ret = mData.size(); mData.push_back(static_cast((i >> 24) & 0xff)); mData.push_back(static_cast((i >> 16) & 0xff)); mData.push_back(static_cast((i >> 8) & 0xff)); @@ -85,7 +85,7 @@ public: int add64(T i) { - int ret = mData.size(); + int const ret = mData.size(); mData.push_back(static_cast((i >> 56) & 0xff)); mData.push_back(static_cast((i >> 48) & 0xff)); mData.push_back(static_cast((i >> 40) & 0xff)); @@ -299,7 +299,7 @@ template int Serializer::addVL(Iter begin, Iter end, int len) { - int ret = addEncoded(len); + int const ret = addEncoded(len); for (; begin != end; ++begin) { addRaw(begin->data(), begin->size()); @@ -336,7 +336,7 @@ public: static_assert(N > 0, ""); } - std::size_t + [[nodiscard]] bool empty() const noexcept { return remain_ == 0; diff --git a/include/xrpl/protocol/SystemParameters.h b/include/xrpl/protocol/SystemParameters.h index 70ab67c56f..b51e011533 100644 --- a/include/xrpl/protocol/SystemParameters.h +++ b/include/xrpl/protocol/SystemParameters.h @@ -14,7 +14,7 @@ namespace xrpl { static inline std::string const& systemName() { - static std::string const name = "ripple"; + static std::string const name = "xrpld"; return name; } diff --git a/include/xrpl/protocol/TxSearched.h b/include/xrpl/protocol/TxSearched.h index e085bff315..c3359ee5ac 100644 --- a/include/xrpl/protocol/TxSearched.h +++ b/include/xrpl/protocol/TxSearched.h @@ -2,6 +2,6 @@ namespace xrpl { -enum class TxSearched { all, some, unknown }; +enum class TxSearched { All, Some, Unknown }; -} +} // namespace xrpl diff --git a/include/xrpl/protocol/Units.h b/include/xrpl/protocol/Units.h index b377c80e26..196464fa16 100644 --- a/include/xrpl/protocol/Units.h +++ b/include/xrpl/protocol/Units.h @@ -21,7 +21,7 @@ namespace unit { struct dropTag; /** "fee levels" are used by the transaction queue to compare the relative cost of transactions that require different levels of effort to process. - See also: src/ripple/app/misc/FeeEscalation.md#fee-level */ + See also: src/xrpld/app/misc/FeeEscalation.md#fee-level */ struct feelevelTag; /** unitless values are plain scalars wrapped in a ValueUnit. They are used for calculations in this header. */ diff --git a/include/xrpl/protocol/detail/STVar.h b/include/xrpl/protocol/detail/STVar.h index b5373d1a4a..1131437850 100644 --- a/include/xrpl/protocol/detail/STVar.h +++ b/include/xrpl/protocol/detail/STVar.h @@ -25,16 +25,10 @@ extern nonPresentObject_t nonPresentObject; // Concept to constrain STVar constructors, which // instantiate ST* types from SerializedTypeID -// clang-format off template concept ValidConstructSTArgs = - (std::is_same_v< - std::tuple...>, - std::tuple> || - std::is_same_v< - std::tuple...>, - std::tuple>); -// clang-format on + (std::is_same_v...>, std::tuple> || + std::is_same_v...>, std::tuple>); // "variant" that can hold any type of serialized object // and includes a small-object allocation optimization. @@ -44,7 +38,7 @@ private: // The largest "small object" we can accommodate static std::size_t constexpr max_size = 72; - std::aligned_storage::type d_; + std::aligned_storage::type d_ = {}; STBase* p_ = nullptr; public: @@ -56,7 +50,7 @@ public: STVar& operator=(STVar&& rhs); - STVar(STBase&& t) + STVar(STBase&& t) // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) { p_ = t.move(max_size, &d_); } diff --git a/include/xrpl/protocol/detail/b58_utils.h b/include/xrpl/protocol/detail/b58_utils.h index adb889ee53..5ad12d56e3 100644 --- a/include/xrpl/protocol/detail/b58_utils.h +++ b/include/xrpl/protocol/detail/b58_utils.h @@ -59,7 +59,7 @@ inplace_bigint_add(std::span a, std::uint64_t b) return TokenCodecErrc::inputTooSmall; } - std::uint64_t carry; + std::uint64_t carry = 0; std::tie(a[0], carry) = carrying_add(a[0], b); for (auto& v : a.subspan(1)) @@ -162,7 +162,7 @@ b58_10_to_b58_be(std::uint64_t input) int i = 0; while (input > 0) { - std::uint64_t rem; + std::uint64_t rem = 0; std::tie(input, rem) = div_rem(input, 58); result[resultSize - 1 - i] = rem; i += 1; diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index a40d524c70..c697629e59 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -11,65 +11,65 @@ #error "undefined macro: XRPL_RETIRE_FIX" #endif -// clang-format off // Add new amendments to the top of this list. // Keep it sorted in reverse chronological order. -XRPL_FIX (PermissionedDomainInvariant, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (ExpiredNFTokenOfferRemoval, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (BatchInnerSigs, Supported::no, VoteBehavior::DefaultNo) -XRPL_FEATURE(LendingProtocol, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FEATURE(PermissionDelegationV1_1, Supported::no, VoteBehavior::DefaultNo) -XRPL_FIX (DirectoryLimit, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (IncludeKeyletFields, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FEATURE(DynamicMPT, Supported::no, VoteBehavior::DefaultNo) -XRPL_FIX (TokenEscrowV1, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (PriceOracleOrder, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (MPTDeliveredAmount, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (AMMClawbackRounding, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FEATURE(TokenEscrow, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (AMMv1_3, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FEATURE(PermissionedDEX, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FEATURE(Batch, Supported::no, VoteBehavior::DefaultNo) -XRPL_FEATURE(SingleAssetVault, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (PayChanCancelAfter, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (Security3_1_3, Supported::no, VoteBehavior::DefaultNo) +XRPL_FIX (PermissionedDomainInvariant, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (ExpiredNFTokenOfferRemoval, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (BatchInnerSigs, Supported::no, VoteBehavior::DefaultNo) +XRPL_FEATURE(LendingProtocol, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(PermissionDelegationV1_1, Supported::no, VoteBehavior::DefaultNo) +XRPL_FIX (DirectoryLimit, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (IncludeKeyletFields, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(DynamicMPT, Supported::no, VoteBehavior::DefaultNo) +XRPL_FIX (TokenEscrowV1, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (PriceOracleOrder, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (MPTDeliveredAmount, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (AMMClawbackRounding, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(TokenEscrow, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (AMMv1_3, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(PermissionedDEX, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(Batch, Supported::no, VoteBehavior::DefaultNo) +XRPL_FEATURE(SingleAssetVault, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (PayChanCancelAfter, Supported::yes, VoteBehavior::DefaultNo) // Check flags in Credential transactions -XRPL_FIX (InvalidTxFlags, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (FrozenLPTokenTransfer, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FEATURE(DeepFreeze, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FEATURE(PermissionedDomains, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FEATURE(DynamicNFT, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FEATURE(Credentials, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FEATURE(AMMClawback, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (AMMv1_2, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FEATURE(MPTokensV1, Supported::yes, VoteBehavior::DefaultNo) -// InvariantsV1_1 will be changes to Supported::yes when all the -// invariants expected to be included under it are complete. -XRPL_FEATURE(InvariantsV1_1, Supported::no, VoteBehavior::DefaultNo) -XRPL_FIX (NFTokenPageLinks, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (InnerObjTemplate2, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (EnforceNFTokenTrustline, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (ReducedOffersV2, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FEATURE(NFTokenMintOffer, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (AMMv1_1, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (PreviousTxnID, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (XChainRewardRounding, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (EmptyDID, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FEATURE(PriceOracle, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (AMMOverflowOffer, Supported::yes, VoteBehavior::DefaultYes) -XRPL_FIX (InnerObjTemplate, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (NFTokenReserve, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (FillOrKill, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FEATURE(DID, Supported::yes, VoteBehavior::DefaultNo) -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) +XRPL_FIX (InvalidTxFlags, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (FrozenLPTokenTransfer, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(DeepFreeze, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(PermissionedDomains, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(DynamicNFT, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(Credentials, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(AMMClawback, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (AMMv1_2, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(MPTokensV1, Supported::yes, VoteBehavior::DefaultNo) +// InvariantsV1_1 will be changed to Supported::yes when all the invariants expected to be included +// under it are complete. +XRPL_FEATURE(InvariantsV1_1, Supported::no, VoteBehavior::DefaultNo) +XRPL_FIX (NFTokenPageLinks, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (InnerObjTemplate2, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (EnforceNFTokenTrustline, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (ReducedOffersV2, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(NFTokenMintOffer, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (AMMv1_1, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (PreviousTxnID, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (XChainRewardRounding, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (EmptyDID, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(PriceOracle, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (AMMOverflowOffer, Supported::yes, VoteBehavior::DefaultYes) +XRPL_FIX (InnerObjTemplate, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (NFTokenReserve, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (FillOrKill, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(DID, Supported::yes, VoteBehavior::DefaultNo) +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) // The following amendments are obsolete, but must remain supported // because they could potentially get enabled. @@ -143,5 +143,3 @@ XRPL_RETIRE_FEATURE(SortedDirectories) XRPL_RETIRE_FEATURE(TicketBatch) XRPL_RETIRE_FEATURE(TickSize) XRPL_RETIRE_FEATURE(TrustSetAuth) - -// clang-format on diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index 216f404bec..7955dcb66c 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -578,7 +578,7 @@ LEDGER_ENTRY(ltLOAN, 0x0089, Loan, loan, ({ // The unrounded true total value of the loan. // // - TrueTotalPrincipalOutstanding can be computed using the algorithm - // in the ripple::detail::loanPrincipalFromPeriodicPayment function. + // in the xrpl::detail::loanPrincipalFromPeriodicPayment function. // // - TrueTotalInterestOutstanding = TrueTotalLoanValue - // TrueTotalPrincipalOutstanding diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 712cf568af..a203f2fe8c 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -5,7 +5,6 @@ #error "undefined macro: TYPED_SFIELD" #endif -// clang-format off // untyped UNTYPED_SFIELD(sfLedgerEntry, LEDGERENTRY, 257) @@ -421,5 +420,3 @@ UNTYPED_SFIELD(sfAcceptedCredentials, ARRAY, 28) UNTYPED_SFIELD(sfPermissions, ARRAY, 29) UNTYPED_SFIELD(sfRawTransactions, ARRAY, 30) UNTYPED_SFIELD(sfBatchSigners, ARRAY, 31, SField::sMD_Default, SField::notSigning) - -// clang-format on diff --git a/include/xrpl/protocol/detail/token_errors.h b/include/xrpl/protocol/detail/token_errors.h index 686851f508..54b283c41a 100644 --- a/include/xrpl/protocol/detail/token_errors.h +++ b/include/xrpl/protocol/detail/token_errors.h @@ -15,7 +15,7 @@ enum class TokenCodecErrc { overflowAdd, unknown, }; -} +} // namespace xrpl namespace std { template <> @@ -69,7 +69,7 @@ public: inline xrpl::detail::TokenCodecErrcCategory const& TokenCodecErrcCategory() { - static xrpl::detail::TokenCodecErrcCategory c; + static xrpl::detail::TokenCodecErrcCategory const c; return c; } diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index 08832acde7..a2f3c1be43 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -42,7 +42,7 @@ TRANSACTION(ttPAYMENT, 0, Payment, /** This transaction type creates an escrow object. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, Delegation::delegable, @@ -58,6 +58,9 @@ TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, })) /** This transaction type completes an existing escrow. */ +#if TRANSACTION_INCLUDE +# include +#endif TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, Delegation::delegable, uint256{}, @@ -73,7 +76,7 @@ TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, /** This transaction type adjusts various account settings. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttACCOUNT_SET, 3, AccountSet, Delegation::notDelegable, @@ -94,7 +97,7 @@ TRANSACTION(ttACCOUNT_SET, 3, AccountSet, /** This transaction type cancels an existing escrow. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel, Delegation::delegable, @@ -121,7 +124,7 @@ TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey, /** This transaction type creates an offer to trade one asset for another. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttOFFER_CREATE, 7, OfferCreate, Delegation::delegable, @@ -137,7 +140,7 @@ TRANSACTION(ttOFFER_CREATE, 7, OfferCreate, /** This transaction type cancels existing offers to trade one asset for another. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel, Delegation::delegable, @@ -151,7 +154,7 @@ TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel, /** This transaction type creates a new set of tickets. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, Delegation::delegable, @@ -167,7 +170,7 @@ TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, // The SignerEntries are optional because a SignerList is deleted by // setting the SignerQuorum to zero and omitting SignerEntries. #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet, Delegation::notDelegable, @@ -180,7 +183,7 @@ TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet, /** This transaction type creates a new unidirectional XRP payment channel. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, Delegation::delegable, @@ -196,6 +199,9 @@ TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, })) /** This transaction type funds an existing unidirectional XRP payment channel. */ +#if TRANSACTION_INCLUDE +# include +#endif TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund, Delegation::delegable, uint256{}, @@ -207,6 +213,9 @@ TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund, })) /** This transaction type submits a claim against an existing unidirectional payment channel. */ +#if TRANSACTION_INCLUDE +# include +#endif TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, Delegation::delegable, uint256{}, @@ -222,7 +231,7 @@ TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, /** This transaction type creates a new check. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, Delegation::delegable, @@ -238,7 +247,7 @@ TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, /** This transaction type cashes an existing check. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttCHECK_CASH, 17, CheckCash, Delegation::delegable, @@ -252,7 +261,7 @@ TRANSACTION(ttCHECK_CASH, 17, CheckCash, /** This transaction type cancels an existing check. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel, Delegation::delegable, @@ -279,7 +288,7 @@ TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth, /** This transaction type modifies a trustline between two accounts. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttTRUST_SET, 20, TrustSet, Delegation::delegable, @@ -293,7 +302,7 @@ TRANSACTION(ttTRUST_SET, 20, TrustSet, /** This transaction type deletes an existing account. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete, Delegation::notDelegable, @@ -617,7 +626,7 @@ TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge, /** This transaction type creates or updates a DID */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttDID_SET, 49, DIDSet, Delegation::delegable, @@ -630,6 +639,9 @@ TRANSACTION(ttDID_SET, 49, DIDSet, })) /** This transaction type deletes a DID */ +#if TRANSACTION_INCLUDE +# include +#endif TRANSACTION(ttDID_DELETE, 50, DIDDelete, Delegation::delegable, featureDID, @@ -638,7 +650,7 @@ TRANSACTION(ttDID_DELETE, 50, DIDDelete, /** This transaction type creates an Oracle instance */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttORACLE_SET, 51, OracleSet, Delegation::delegable, @@ -655,7 +667,7 @@ TRANSACTION(ttORACLE_SET, 51, OracleSet, /** This transaction type deletes an Oracle instance */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttORACLE_DELETE, 52, OracleDelete, Delegation::delegable, @@ -739,7 +751,7 @@ TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize, /** This transaction type create an Credential instance */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate, Delegation::delegable, @@ -753,6 +765,9 @@ TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate, })) /** This transaction type accept an Credential object */ +#if TRANSACTION_INCLUDE +# include +#endif TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept, Delegation::delegable, featureCredentials, @@ -763,6 +778,9 @@ TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept, })) /** This transaction type delete an Credential object */ +#if TRANSACTION_INCLUDE +# include +#endif TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete, Delegation::delegable, featureCredentials, @@ -830,7 +848,7 @@ TRANSACTION(ttDELEGATE_SET, 64, DelegateSet, # include #endif TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, createPseudoAcct | createMPTIssuance | mustModifyVault, ({ @@ -848,7 +866,7 @@ TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, # include #endif TRANSACTION(ttVAULT_SET, 66, VaultSet, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, mustModifyVault, ({ @@ -863,7 +881,7 @@ TRANSACTION(ttVAULT_SET, 66, VaultSet, # include #endif TRANSACTION(ttVAULT_DELETE, 67, VaultDelete, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, mustDeleteAcct | destroyMPTIssuance | mustModifyVault, ({ @@ -875,7 +893,7 @@ TRANSACTION(ttVAULT_DELETE, 67, VaultDelete, # include #endif TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, mayAuthorizeMPT | mustModifyVault, ({ @@ -888,7 +906,7 @@ TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit, # include #endif TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, mayDeleteMPT | mayAuthorizeMPT | mustModifyVault, ({ @@ -903,7 +921,7 @@ TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, # include #endif TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, mayDeleteMPT | mustModifyVault, ({ @@ -932,7 +950,7 @@ TRANSACTION(ttBATCH, 71, Batch, # include #endif TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, createPseudoAcct | mayAuthorizeMPT, ({ {sfVaultID, soeREQUIRED}, @@ -949,7 +967,7 @@ TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet, # include #endif TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, mustDeleteAcct | mayAuthorizeMPT, ({ {sfLoanBrokerID, soeREQUIRED}, @@ -960,7 +978,7 @@ TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete, # include #endif TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, noPriv, ({ {sfLoanBrokerID, soeREQUIRED}, @@ -972,7 +990,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit, # include #endif TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, mayAuthorizeMPT, ({ {sfLoanBrokerID, soeREQUIRED}, @@ -987,7 +1005,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw, # include #endif TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, noPriv, ({ {sfLoanBrokerID, soeOPTIONAL}, @@ -999,7 +1017,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback, # include #endif TRANSACTION(ttLOAN_SET, 80, LoanSet, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, mayAuthorizeMPT | mustModifyVault, ({ {sfLoanBrokerID, soeREQUIRED}, @@ -1026,7 +1044,7 @@ TRANSACTION(ttLOAN_SET, 80, LoanSet, # include #endif TRANSACTION(ttLOAN_DELETE, 81, LoanDelete, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, noPriv, ({ {sfLoanID, soeREQUIRED}, @@ -1037,7 +1055,7 @@ TRANSACTION(ttLOAN_DELETE, 81, LoanDelete, # include #endif TRANSACTION(ttLOAN_MANAGE, 82, LoanManage, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, // All of the LoanManage options will modify the vault, but the // transaction can succeed without options, essentially making it @@ -1051,7 +1069,7 @@ TRANSACTION(ttLOAN_MANAGE, 82, LoanManage, # include #endif TRANSACTION(ttLOAN_PAY, 84, LoanPay, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, mayAuthorizeMPT | mustModifyVault, ({ {sfLoanID, soeREQUIRED}, diff --git a/include/xrpl/protocol/digest.h b/include/xrpl/protocol/digest.h index b5ec277e7b..7664069e1c 100644 --- a/include/xrpl/protocol/digest.h +++ b/include/xrpl/protocol/digest.h @@ -40,7 +40,7 @@ public: operator result_type() noexcept; private: - char ctx_[96]; + char ctx_[96]{}; }; /** SHA-512 digest @@ -63,7 +63,7 @@ public: operator result_type() noexcept; private: - char ctx_[216]; + char ctx_[216]{}; }; /** SHA-256 digest @@ -86,7 +86,7 @@ public: operator result_type() noexcept; private: - char ctx_[112]; + char ctx_[112]{}; }; //------------------------------------------------------------------------------ @@ -100,7 +100,7 @@ using sha512_hasher = openssl_sha512_hasher; /** Returns the RIPEMD-160 digest of the SHA256 hash of the message. This operation is used to compute the 160-bit identifier - representing a Ripple account, from a message. Typically the + representing an XRPL account, from a message. Typically the message is the public key of the account - which is not stored in the account root. diff --git a/include/xrpl/protocol/json_get_or_throw.h b/include/xrpl/protocol/json_get_or_throw.h index 336d875450..4406d9ff24 100644 --- a/include/xrpl/protocol/json_get_or_throw.h +++ b/include/xrpl/protocol/json_get_or_throw.h @@ -112,7 +112,7 @@ getOrThrow(Json::Value const& v, xrpl::SField const& field) { auto const s = inner.asString(); // parse as hex - std::uint64_t val; + std::uint64_t val = 0; auto [p, ec] = std::from_chars(s.data(), s.data() + s.size(), val, 16); diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index 5c605dde04..ecd9d65b4e 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -21,684 +21,660 @@ namespace jss { error: Common properties of RPC error responses. */ -// clang-format off -JSS(AL_size); // out: GetCounts -JSS(AL_hit_rate); // out: GetCounts -JSS(AcceptedCredentials); // out: AccountObjects -JSS(ACCOUNT_SET_FLAGS); // out: RPC server_definitions -JSS(Account); // in: TransactionSign; field. -JSS(AMMID); // field -JSS(Amount); // in: TransactionSign; field. -JSS(Amount2); // in/out: AMM IOU/XRP pool, deposit, withdraw amount -JSS(Asset); // in: AMM Asset1 -JSS(Asset2); // in: AMM Asset2 -JSS(AssetClass); // in: Oracle -JSS(AssetPrice); // in: Oracle -JSS(AuthAccount); // in: AMM Auction Slot -JSS(AuthAccounts); // in: AMM Auction Slot -JSS(BaseAsset); // in: Oracle -JSS(BidMax); // in: AMM Bid -JSS(BidMin); // in: AMM Bid -JSS(ClearFlag); // field. -JSS(Counterparty); // field. -JSS(CounterpartySignature);// field. -JSS(DeliverMax); // out: alias to Amount -JSS(DeliverMin); // in: TransactionSign -JSS(Destination); // in: TransactionSign; field. -JSS(EPrice); // in: AMM Deposit option -JSS(Fee); // in/out: TransactionSign; field. -JSS(Flags); // in/out: TransactionSign; field. -JSS(Holder); // field. -JSS(Invalid); // -JSS(Issuer); // in: Credential transactions -JSS(IssuingChainDoor); // field. -JSS(IssuingChainIssue); // field. -JSS(LastLedgerSequence); // in: TransactionSign; field -JSS(LastUpdateTime); // field. -JSS(LimitAmount); // field. -JSS(LockingChainDoor); // field. -JSS(LockingChainIssue); // field. -JSS(NetworkID); // field. -JSS(LPTokenOut); // in: AMM Liquidity Provider deposit tokens -JSS(LPTokenIn); // in: AMM Liquidity Provider withdraw tokens -JSS(LPToken); // out: AMM Liquidity Provider tokens info -JSS(OfferSequence); // field. -JSS(OracleDocumentID); // field -JSS(Owner); // field -JSS(Paths); // in/out: TransactionSign -JSS(PriceDataSeries); // field. -JSS(PriceData); // field. -JSS(Provider); // field. -JSS(QuoteAsset); // in: Oracle. -JSS(RawTransaction); // in: Batch -JSS(RawTransactions); // in: Batch -JSS(SLE_hit_rate); // out: GetCounts. -JSS(Scale); // field. -JSS(SettleDelay); // in: TransactionSign -JSS(SendMax); // in: TransactionSign -JSS(Sequence); // in/out: TransactionSign; field. -JSS(SetFlag); // field. -JSS(Signer); // field. -JSS(Signers); // field. -JSS(SigningPubKey); // field. -JSS(Subject); // in: Credential transactions -JSS(TakerGets); // field. -JSS(TakerPays); // field. -JSS(TradingFee); // in/out: AMM trading fee -JSS(TransactionType); // in: TransactionSign. -JSS(TransferRate); // in: TransferRate. -JSS(TxnSignature); // field. -JSS(URI); // field. -JSS(VoteSlots); // out: AMM Vote -JSS(aborted); // out: InboundLedger -JSS(accepted); // out: LedgerToJson, OwnerInfo, SubmitTransaction -JSS(accountState); // out: LedgerToJson -JSS(accountTreeHash); // out: ledger/Ledger.cpp -JSS(account_data); // out: AccountInfo -JSS(account_flags); // out: AccountInfo -JSS(account_hash); // out: LedgerToJson -JSS(account_id); // out: WalletPropose -JSS(account_nfts); // out: AccountNFTs -JSS(account_objects); // out: AccountObjects -JSS(account_root); // in: LedgerEntry -JSS(account_sequence_next); // out: SubmitTransaction +JSS(AL_size); // out: GetCounts +JSS(AL_hit_rate); // out: GetCounts +JSS(AcceptedCredentials); // out: AccountObjects +JSS(ACCOUNT_SET_FLAGS); // out: RPC server_definitions +JSS(Account); // in: TransactionSign; field. +JSS(AMMID); // field +JSS(Amount); // in: TransactionSign; field. +JSS(Amount2); // in/out: AMM IOU/XRP pool, deposit, withdraw amount +JSS(Asset); // in: AMM Asset1 +JSS(Asset2); // in: AMM Asset2 +JSS(AssetClass); // in: Oracle +JSS(AssetPrice); // in: Oracle +JSS(AuthAccount); // in: AMM Auction Slot +JSS(AuthAccounts); // in: AMM Auction Slot +JSS(BaseAsset); // in: Oracle +JSS(BidMax); // in: AMM Bid +JSS(BidMin); // in: AMM Bid +JSS(ClearFlag); // field. +JSS(Counterparty); // field. +JSS(CounterpartySignature); // field. +JSS(DeliverMax); // out: alias to Amount +JSS(DeliverMin); // in: TransactionSign +JSS(Destination); // in: TransactionSign; field. +JSS(EPrice); // in: AMM Deposit option +JSS(Fee); // in/out: TransactionSign; field. +JSS(Flags); // in/out: TransactionSign; field. +JSS(Holder); // field. +JSS(Invalid); // +JSS(Issuer); // in: Credential transactions +JSS(IssuingChainDoor); // field. +JSS(IssuingChainIssue); // field. +JSS(LastLedgerSequence); // in: TransactionSign; field +JSS(LastUpdateTime); // field. +JSS(LimitAmount); // field. +JSS(LockingChainDoor); // field. +JSS(LockingChainIssue); // field. +JSS(NetworkID); // field. +JSS(LPTokenOut); // in: AMM Liquidity Provider deposit tokens +JSS(LPTokenIn); // in: AMM Liquidity Provider withdraw tokens +JSS(LPToken); // out: AMM Liquidity Provider tokens info +JSS(OfferSequence); // field. +JSS(OracleDocumentID); // field +JSS(Owner); // field +JSS(Paths); // in/out: TransactionSign +JSS(PriceDataSeries); // field. +JSS(PriceData); // field. +JSS(Provider); // field. +JSS(QuoteAsset); // in: Oracle. +JSS(RawTransaction); // in: Batch +JSS(RawTransactions); // in: Batch +JSS(SLE_hit_rate); // out: GetCounts. +JSS(Scale); // field. +JSS(SettleDelay); // in: TransactionSign +JSS(SendMax); // in: TransactionSign +JSS(Sequence); // in/out: TransactionSign; field. +JSS(SetFlag); // field. +JSS(Signer); // field. +JSS(Signers); // field. +JSS(SigningPubKey); // field. +JSS(Subject); // in: Credential transactions +JSS(TakerGets); // field. +JSS(TakerPays); // field. +JSS(TradingFee); // in/out: AMM trading fee +JSS(TransactionType); // in: TransactionSign. +JSS(TransferRate); // in: TransferRate. +JSS(TxnSignature); // field. +JSS(URI); // field. +JSS(VoteSlots); // out: AMM Vote +JSS(aborted); // out: InboundLedger +JSS(accepted); // out: LedgerToJson, OwnerInfo, SubmitTransaction +JSS(accountState); // out: LedgerToJson +JSS(accountTreeHash); // out: ledger/Ledger.cpp +JSS(account_data); // out: AccountInfo +JSS(account_flags); // out: AccountInfo +JSS(account_hash); // out: LedgerToJson +JSS(account_id); // out: WalletPropose +JSS(account_nfts); // out: AccountNFTs +JSS(account_objects); // out: AccountObjects +JSS(account_root); // in: LedgerEntry +JSS(account_sequence_next); // out: SubmitTransaction JSS(account_sequence_available); // out: SubmitTransaction JSS(account_history_tx_stream); // in: Subscribe, Unsubscribe JSS(account_history_tx_index); // out: Account txn history subscribe - -JSS(account_history_tx_first); // out: Account txn history subscribe -JSS(account_history_boundary); // out: Account txn history subscribe -JSS(accounts); // in: LedgerEntry, Subscribe, - // handlers/Ledger, Unsubscribe -JSS(accounts_proposed); // in: Subscribe, Unsubscribe -JSS(action); -JSS(acquiring); // out: LedgerRequest -JSS(address); // out: PeerImp -JSS(affected); // out: AcceptedLedgerTx -JSS(age); // out: NetworkOPs, Peers -JSS(alternatives); // out: PathRequest, RipplePathFind -JSS(amendment_blocked); // out: NetworkOPs -JSS(amm_account); // in: amm_info -JSS(amount); // out: AccountChannels, amm_info -JSS(amount2); // out: amm_info -JSS(api_version); // in: many, out: Version -JSS(api_version_low); // out: Version -JSS(applied); // out: SubmitTransaction -JSS(asks); // out: Subscribe -JSS(asset); // in: amm_info -JSS(asset2); // in: amm_info -JSS(assets); // out: GatewayBalances -JSS(asset_frozen); // out: amm_info -JSS(asset2_frozen); // out: amm_info -JSS(attestations); -JSS(attestation_reward_account); -JSS(auction_slot); // out: amm_info -JSS(authorized); // out: AccountLines -JSS(authorize); // out: delegate -JSS(authorized_credentials); // in: ledger_entry DepositPreauth -JSS(auth_accounts); // out: amm_info -JSS(auth_change); // out: AccountInfo -JSS(auth_change_queued); // out: AccountInfo -JSS(available); // out: ValidatorList -JSS(avg_bps_recv); // out: Peers -JSS(avg_bps_sent); // out: Peers -JSS(balance); // out: AccountLines -JSS(balances); // out: GatewayBalances -JSS(base); // out: LogLevel -JSS(base_asset); // in: get_aggregate_price -JSS(base_fee); // out: NetworkOPs -JSS(base_fee_xrp); // out: NetworkOPs -JSS(bids); // out: Subscribe -JSS(binary); // in: AccountTX, LedgerEntry, - // AccountTxOld, Tx LedgerData -JSS(blob); // out: ValidatorList -JSS(blobs_v2); // out: ValidatorList - // in: UNL -JSS(books); // in: Subscribe, Unsubscribe -JSS(both); // in: Subscribe, Unsubscribe -JSS(both_sides); // in: Subscribe, Unsubscribe -JSS(branch); // out: server_info -JSS(broadcast); // out: SubmitTransaction -JSS(bridge_account); // in: LedgerEntry -JSS(build_path); // in: TransactionSign -JSS(build_version); // out: NetworkOPs -JSS(cancel_after); // out: AccountChannels -JSS(can_delete); // out: CanDelete -JSS(mpt_amount); // out: mpt_holders -JSS(mpt_issuance_id); // in: Payment, mpt_holders -JSS(mptoken_index); // out: mpt_holders -JSS(changes); // out: BookChanges -JSS(channel_id); // out: AccountChannels -JSS(channels); // out: AccountChannels -JSS(check_nodes); // in: LedgerCleaner -JSS(clear); // in/out: FetchInfo -JSS(close); // out: BookChanges -JSS(close_flags); // out: LedgerToJson -JSS(close_time); // in: Application, out: NetworkOPs, - // RCLCxPeerPos, LedgerToJson -JSS(close_time_iso); // out: Tx, NetworkOPs, TransactionEntry - // AccountTx, LedgerToJson -JSS(close_time_estimated); // in: Application, out: LedgerToJson -JSS(close_time_human); // out: LedgerToJson -JSS(close_time_offset); // out: NetworkOPs -JSS(close_time_resolution); // in: Application; out: LedgerToJson -JSS(closed); // out: NetworkOPs, LedgerToJson, - // handlers/Ledger -JSS(closed_ledger); // out: NetworkOPs -JSS(cluster); // out: PeerImp -JSS(code); // out: errors -JSS(command); // in: RPCHandler -JSS(common); // out: RPC server_definitions -JSS(complete); // out: NetworkOPs, InboundLedger -JSS(complete_ledgers); // out: NetworkOPs, PeerImp -JSS(consensus); // out: NetworkOPs, LedgerConsensus -JSS(converge_time); // out: NetworkOPs -JSS(converge_time_s); // out: NetworkOPs -JSS(cookie); // out: NetworkOPs -JSS(count); // in: AccountTx*, ValidatorList -JSS(counters); // in/out: retrieve counters -JSS(credentials); // in: deposit_authorized -JSS(credential_type); // in: LedgerEntry DepositPreauth -JSS(ctid); // in/out: Tx RPC -JSS(currency_a); // out: BookChanges -JSS(currency_b); // out: BookChanges -JSS(currency); // in: paths/PathRequest, STAmount - // out: STPathSet, STAmount, - // AccountLines -JSS(current); // out: OwnerInfo -JSS(current_activities); -JSS(current_ledger_size); // out: TxQ -JSS(current_queue_size); // out: TxQ -JSS(data); // out: LedgerData -JSS(date); // out: tx/Transaction, NetworkOPs -JSS(dbKBLedger); // out: getCounts -JSS(dbKBTotal); // out: getCounts -JSS(dbKBTransaction); // out: getCounts -JSS(debug_signing); // in: TransactionSign -JSS(deletion_blockers_only); // in: AccountObjects -JSS(delivered_amount); // out: insertDeliveredAmount -JSS(deposit_authorized); // out: deposit_authorized -JSS(deprecated); // out -JSS(descending); // in: AccountTx* -JSS(description); // in/out: Reservations -JSS(destination); // in: nft_buy_offers, nft_sell_offers -JSS(destination_account); // in: PathRequest, RipplePathFind, account_lines - // out: AccountChannels -JSS(destination_amount); // in: PathRequest, RipplePathFind -JSS(destination_currencies); // in: PathRequest, RipplePathFind -JSS(destination_tag); // in: PathRequest - // out: AccountChannels -JSS(details); // out: Manifest, server_info -JSS(dir_entry); // out: DirectoryEntryIterator -JSS(dir_index); // out: DirectoryEntryIterator -JSS(dir_root); // out: DirectoryEntryIterator -JSS(discounted_fee); // out: amm_info -JSS(domain); // out: ValidatorInfo, Manifest -JSS(drops); // out: TxQ -JSS(duration_us); // out: NetworkOPs -JSS(effective); // out: ValidatorList - // in: UNL -JSS(enabled); // out: AmendmentTable -JSS(engine_result); // out: NetworkOPs, TransactionSign, Submit -JSS(engine_result_code); // out: NetworkOPs, TransactionSign, Submit -JSS(engine_result_message); // out: NetworkOPs, TransactionSign, Submit -JSS(entire_set); // out: get_aggregate_price -JSS(ephemeral_key); // out: ValidatorInfo - // in/out: Manifest -JSS(error); // out: error -JSS(errored); -JSS(error_code); // out: error -JSS(error_exception); // out: Submit -JSS(error_message); // out: error -JSS(expand); // in: handler/Ledger -JSS(expected_date); // out: any (warnings) -JSS(expected_date_UTC); // out: any (warnings) -JSS(expected_ledger_size); // out: TxQ -JSS(expiration); // out: AccountOffers, AccountChannels, - // ValidatorList, amm_info -JSS(fail_hard); // in: Sign, Submit -JSS(failed); // out: InboundLedger -JSS(feature); // in: Feature -JSS(features); // out: Feature -JSS(fee_base); // out: NetworkOPs -JSS(fee_div_max); // in: TransactionSign -JSS(fee_level); // out: AccountInfo -JSS(fee_mult_max); // in: TransactionSign -JSS(fee_ref); // out: NetworkOPs, DEPRECATED -JSS(fetch_pack); // out: NetworkOPs -JSS(FIELDS); // out: RPC server_definitions - // matches definitions.json format -JSS(first); // out: rpc/Version -JSS(finished); -JSS(fix_txns); // in: LedgerCleaner -JSS(flags); // out: AccountOffers, - // NetworkOPs -JSS(forward); // in: AccountTx -JSS(freeze); // out: AccountLines -JSS(freeze_peer); // out: AccountLines -JSS(deep_freeze); // out: AccountLines -JSS(deep_freeze_peer); // out: AccountLines -JSS(frozen_balances); // out: GatewayBalances -JSS(full); // in: LedgerClearer, handlers/Ledger -JSS(full_reply); // out: PathFind -JSS(fullbelow_size); // out: GetCounts -JSS(git); // out: server_info -JSS(good); // out: RPCVersion -JSS(hash); // out: NetworkOPs, InboundLedger, - // LedgerToJson, STTx; field -JSS(have_header); // out: InboundLedger -JSS(have_state); // out: InboundLedger -JSS(have_transactions); // out: InboundLedger -JSS(high); // out: BookChanges -JSS(highest_sequence); // out: AccountInfo -JSS(highest_ticket); // out: AccountInfo -JSS(historical_perminute); // historical_perminute. -JSS(holders); // out: MPTHolders -JSS(hostid); // out: NetworkOPs -JSS(hotwallet); // in: GatewayBalances -JSS(id); // websocket. -JSS(ident); // in: AccountCurrencies, AccountInfo, - // OwnerInfo -JSS(ignore_default); // in: AccountLines -JSS(inLedger); // out: tx/Transaction -JSS(inbound); // out: PeerImp -JSS(index); // in: LedgerEntry - // out: STLedgerEntry, - // LedgerEntry, TxHistory, LedgerData -JSS(info); // out: ServerInfo, ConsensusInfo, FetchInfo -JSS(initial_sync_duration_us); -JSS(internal_command); // in: Internal -JSS(invalid_API_version); // out: Many, when a request has an invalid - // version -JSS(io_latency_ms); // out: NetworkOPs -JSS(ip); // in: Connect, out: OverlayImpl -JSS(is_burned); // out: nft_info (clio) -JSS(isSerialized); // out: RPC server_definitions - // matches definitions.json format -JSS(isSigningField); // out: RPC server_definitions - // matches definitions.json format -JSS(isVLEncoded); // out: RPC server_definitions - // matches definitions.json format -JSS(issuer); // in: RipplePathFind, Subscribe, - // Unsubscribe, BookOffers - // out: STPathSet, STAmount -JSS(job); -JSS(job_queue); -JSS(jobs); -JSS(jsonrpc); // json version -JSS(jq_trans_overflow); // JobQueue transaction limit overflow. -JSS(kept); // out: SubmitTransaction -JSS(key); // out -JSS(key_type); // in/out: WalletPropose, TransactionSign -JSS(latency); // out: PeerImp -JSS(last); // out: RPCVersion -JSS(last_close); // out: NetworkOPs -JSS(last_refresh_time); // out: ValidatorSite -JSS(last_refresh_status); // out: ValidatorSite -JSS(last_refresh_message); // out: ValidatorSite -JSS(ledger); // in: NetworkOPs, LedgerCleaner, - // RPCHelpers - // out: NetworkOPs, PeerImp -JSS(ledger_current_index); // out: NetworkOPs, RPCHelpers, - // LedgerCurrent, LedgerAccept, - // AccountLines -JSS(ledger_data); // out: LedgerHeader -JSS(ledger_hash); // in: RPCHelpers, LedgerRequest, - // RipplePathFind, TransactionEntry, - // handlers/Ledger - // out: NetworkOPs, RPCHelpers, - // LedgerClosed, LedgerData, - // AccountLines -JSS(ledger_hit_rate); // out: GetCounts -JSS(ledger_index); // in/out: many -JSS(ledger_index_max); // in, out: AccountTx* -JSS(ledger_index_min); // in, out: AccountTx* -JSS(ledger_max); // in, out: AccountTx* -JSS(ledger_min); // in, out: AccountTx* -JSS(ledger_time); // out: NetworkOPs -JSS(LEDGER_ENTRY_TYPES); // out: RPC server_definitions - // matches definitions.json format -JSS(LEDGER_ENTRY_FLAGS); // out: RPC server_definitions -JSS(LEDGER_ENTRY_FORMATS); // out: RPC server_definitions -JSS(levels); // LogLevels -JSS(limit); // in/out: AccountTx*, AccountOffers, - // AccountLines, AccountObjects - // in: LedgerData, BookOffers -JSS(limit_peer); // out: AccountLines -JSS(lines); // out: AccountLines -JSS(list); // out: ValidatorList -JSS(load); // out: NetworkOPs, PeerImp -JSS(load_base); // out: NetworkOPs -JSS(load_factor); // out: NetworkOPs -JSS(load_factor_cluster); // out: NetworkOPs -JSS(load_factor_fee_escalation); // out: NetworkOPs -JSS(load_factor_fee_queue); // out: NetworkOPs -JSS(load_factor_fee_reference); // out: NetworkOPs -JSS(load_factor_local); // out: NetworkOPs -JSS(load_factor_net); // out: NetworkOPs -JSS(load_factor_server); // out: NetworkOPs -JSS(load_fee); // out: LoadFeeTrackImp, NetworkOPs -JSS(loan_broker_id); // in: LedgerEntry -JSS(loan_seq); // in: LedgerEntry -JSS(local); // out: resource/Logic.h -JSS(local_txs); // out: GetCounts -JSS(local_static_keys); // out: ValidatorList -JSS(locked); // out: GatewayBalances -JSS(low); // out: BookChanges -JSS(lowest_sequence); // out: AccountInfo -JSS(lowest_ticket); // out: AccountInfo -JSS(lp_token); // out: amm_info -JSS(majority); // out: RPC feature -JSS(manifest); // out: ValidatorInfo, Manifest -JSS(marker); // in/out: AccountTx, AccountOffers, - // AccountLines, AccountObjects, - // LedgerData - // in: BookOffers -JSS(master_key); // out: WalletPropose, NetworkOPs, - // ValidatorInfo - // in/out: Manifest -JSS(master_seed); // out: WalletPropose -JSS(master_seed_hex); // out: WalletPropose -JSS(master_signature); // out: pubManifest -JSS(max_ledger); // in/out: LedgerCleaner -JSS(max_queue_size); // out: TxQ -JSS(max_spend_drops); // out: AccountInfo -JSS(max_spend_drops_total); // out: AccountInfo -JSS(mean); // out: get_aggregate_price -JSS(median); // out: get_aggregate_price -JSS(median_fee); // out: TxQ -JSS(median_level); // out: TxQ -JSS(message); // error. -JSS(meta); // out: NetworkOPs, AccountTx*, Tx -JSS(meta_blob); // out: NetworkOPs, AccountTx*, Tx -JSS(metaData); -JSS(metadata); // out: TransactionEntry -JSS(method); // RPC -JSS(methods); -JSS(metrics); // out: Peers -JSS(min_count); // in: GetCounts -JSS(min_ledger); // in: LedgerCleaner -JSS(minimum_fee); // out: TxQ -JSS(minimum_level); // out: TxQ -JSS(missingCommand); // error -JSS(name); // out: AmendmentTableImpl, PeerImp -JSS(needed_state_hashes); // out: InboundLedger -JSS(needed_transaction_hashes); // out: InboundLedger -JSS(network_id); // out: NetworkOPs -JSS(network_ledger); // out: NetworkOPs -JSS(next_refresh_time); // out: ValidatorSite -JSS(nft_id); // in: nft_sell_offers, nft_buy_offers -JSS(nft_offer_index); // out nft_buy_offers, nft_sell_offers -JSS(nft_serial); // out: account_nfts -JSS(nft_taxon); // out: nft_info (clio) -JSS(nftoken_id); // out: insertNFTokenID -JSS(nftoken_ids); // out: insertNFTokenID -JSS(no_ripple); // out: AccountLines -JSS(no_ripple_peer); // out: AccountLines -JSS(node); // out: LedgerEntry -JSS(node_binary); // out: LedgerEntry -JSS(node_read_bytes); // out: GetCounts -JSS(node_read_errors); // out: GetCounts -JSS(node_read_retries); // out: GetCounts -JSS(node_reads_hit); // out: GetCounts -JSS(node_reads_total); // out: GetCounts -JSS(node_reads_duration_us); // out: GetCounts -JSS(node_size); // out: server_info -JSS(nodes); // out: VaultInfo -JSS(nodestore); // out: GetCounts -JSS(node_writes); // out: GetCounts -JSS(node_written_bytes); // out: GetCounts -JSS(node_writes_duration_us); // out: GetCounts -JSS(node_write_retries); // out: GetCounts -JSS(node_writes_delayed); // out::GetCounts -JSS(nth); // out: RPC server_definitions -JSS(obligations); // out: GatewayBalances -JSS(offers); // out: NetworkOPs, AccountOffers, Subscribe -JSS(offer_id); // out: insertNFTokenOfferID -JSS(offline); // in: TransactionSign -JSS(offset); // in/out: AccountTxOld -JSS(open); // out: handlers/Ledger -JSS(open_ledger_cost); // out: SubmitTransaction -JSS(open_ledger_fee); // out: TxQ -JSS(open_ledger_level); // out: TxQ -JSS(optionality); // out: server_definitions -JSS(oracles); // in: get_aggregate_price -JSS(oracle_document_id); // in: get_aggregate_price -JSS(owner); // in: LedgerEntry, out: NetworkOPs -JSS(owner_funds); // in/out: Ledger, NetworkOPs, AcceptedLedgerTx -JSS(page_index); -JSS(params); // RPC -JSS(parent_close_time); // out: LedgerToJson -JSS(parent_hash); // out: LedgerToJson -JSS(partition); // in: LogLevel -JSS(passphrase); // in: WalletPropose -JSS(password); // in: Subscribe -JSS(paths); // in: RipplePathFind -JSS(paths_canonical); // out: RipplePathFind -JSS(paths_computed); // out: PathRequest, RipplePathFind -JSS(peer); // in: AccountLines -JSS(peer_authorized); // out: AccountLines -JSS(peer_id); // out: RCLCxPeerPos -JSS(peers); // out: InboundLedger, handlers/Peers, Overlay -JSS(peer_disconnects); // Severed peer connection counter. -JSS(peer_disconnects_resources); // Severed peer connections because of - // excess resource consumption. -JSS(port); // in: Connect, out: NetworkOPs -JSS(ports); // out: NetworkOPs -JSS(previous); // out: Reservations -JSS(previous_ledger); // out: LedgerPropose -JSS(price); // out: amm_info, AuctionSlot -JSS(proof); // in: BookOffers -JSS(propose_seq); // out: LedgerPropose -JSS(proposers); // out: NetworkOPs, LedgerConsensus -JSS(protocol); // out: NetworkOPs, PeerImp -JSS(proxied); // out: RPC ping -JSS(pseudo_account); // out: AccountInfo -JSS(pubkey_node); // out: NetworkOPs -JSS(pubkey_publisher); // out: ValidatorList -JSS(pubkey_validator); // out: NetworkOPs, ValidatorList -JSS(public_key); // out: OverlayImpl, PeerImp, WalletPropose, - // ValidatorInfo - // in/out: Manifest -JSS(public_key_hex); // out: WalletPropose -JSS(published_ledger); // out: NetworkOPs -JSS(publisher_lists); // out: ValidatorList -JSS(quality); // out: NetworkOPs -JSS(quality_in); // out: AccountLines -JSS(quality_out); // out: AccountLines -JSS(queue); // in: AccountInfo -JSS(queue_data); // out: AccountInfo -JSS(queued); // out: SubmitTransaction -JSS(queued_duration_us); -JSS(quote_asset); // in: get_aggregate_price -JSS(random); // out: Random -JSS(raw_meta); // out: AcceptedLedgerTx -JSS(receive_currencies); // out: AccountCurrencies -JSS(reference_level); // out: TxQ -JSS(refresh_interval); // in: UNL -JSS(refresh_interval_min); // out: ValidatorSites -JSS(regular_seed); // in/out: LedgerEntry -JSS(remaining); // out: ValidatorList -JSS(remote); // out: Logic.h -JSS(request); // RPC -JSS(requested); // out: Manifest -JSS(reservations); // out: Reservations -JSS(reserve_base); // out: NetworkOPs -JSS(reserve_base_xrp); // out: NetworkOPs -JSS(reserve_inc); // out: NetworkOPs -JSS(reserve_inc_xrp); // out: NetworkOPs -JSS(response); // websocket -JSS(result); // RPC -JSS(ripple_lines); // out: NetworkOPs -JSS(ripple_state); // in: LedgerEntr -JSS(ripplerpc); // ripple RPC version -JSS(role); // out: Ping.cpp -JSS(rpc); -JSS(rt_accounts); // in: Subscribe, Unsubscribe -JSS(running_duration_us); -JSS(search_depth); // in: RipplePathFind -JSS(searched_all); // out: Tx -JSS(secret); // in: TransactionSign, - // ValidationCreate, ValidationSeed, - // channel_authorize -JSS(seed); // -JSS(seed_hex); // in: WalletPropose, TransactionSign -JSS(send_currencies); // out: AccountCurrencies -JSS(send_max); // in: PathRequest, RipplePathFind -JSS(seq); // in: LedgerEntry; - // out: NetworkOPs, RPCSub, AccountOffers, - // ValidatorList, ValidatorInfo, Manifest -JSS(sequence); // in: UNL -JSS(sequence_count); // out: AccountInfo -JSS(server_domain); // out: NetworkOPs -JSS(server_state); // out: NetworkOPs -JSS(server_state_duration_us);// out: NetworkOPs -JSS(server_status); // out: NetworkOPs -JSS(server_version); // out: NetworkOPs -JSS(settle_delay); // out: AccountChannels -JSS(severity); // in: LogLevel -JSS(shares); // out: VaultInfo -JSS(signature); // out: NetworkOPs, ChannelAuthorize -JSS(signature_target); // in: TransactionSign -JSS(signature_verified); // out: ChannelVerify -JSS(signing_key); // out: NetworkOPs -JSS(signing_keys); // out: ValidatorList -JSS(signing_time); // out: NetworkOPs -JSS(signer_lists); // in/out: AccountInfo -JSS(size); // out: get_aggregate_price -JSS(snapshot); // in: Subscribe -JSS(source_account); // in: PathRequest, RipplePathFind -JSS(source_amount); // in: PathRequest, RipplePathFind -JSS(source_currencies); // in: PathRequest, RipplePathFind -JSS(source_tag); // out: AccountChannels -JSS(stand_alone); // out: NetworkOPs -JSS(standard_deviation); // out: get_aggregate_price -JSS(start); // in: TxHistory -JSS(started); -JSS(state_accounting); // out: NetworkOPs -JSS(state_now); // in: Subscribe -JSS(status); // error -JSS(stop); // in: LedgerCleaner -JSS(stop_history_tx_only); // in: Unsubscribe, stop history tx stream -JSS(streams); // in: Subscribe, Unsubscribe -JSS(strict); // in: AccountCurrencies, AccountInfo -JSS(sub_index); // in: LedgerEntry -JSS(subcommand); // in: PathFind -JSS(subject); // in: LedgerEntry Credential -JSS(success); // rpc -JSS(supported); // out: AmendmentTableImpl -JSS(sync_mode); // in: Submit -JSS(system_time_offset); // out: NetworkOPs -JSS(tag); // out: Peers -JSS(taker); // in: Subscribe, BookOffers -JSS(taker_gets); // in: Subscribe, Unsubscribe, BookOffers -JSS(taker_gets_funded); // out: NetworkOPs -JSS(taker_pays); // in: Subscribe, Unsubscribe, BookOffers -JSS(taker_pays_funded); // out: NetworkOPs -JSS(threshold); // in: Blacklist -JSS(ticket_count); // out: AccountInfo -JSS(ticket_seq); // in: LedgerEntry -JSS(time); -JSS(timeouts); // out: InboundLedger -JSS(time_threshold); // in/out: Oracle aggregate -JSS(time_interval); // out: AMM Auction Slot -JSS(track); // out: PeerImp -JSS(traffic); // out: Overlay -JSS(trim); // in: get_aggregate_price -JSS(trimmed_set); // out: get_aggregate_price -JSS(total); // out: counters -JSS(total_bytes_recv); // out: Peers -JSS(total_bytes_sent); // out: Peers -JSS(total_coins); // out: LedgerToJson -JSS(trading_fee); // out: amm_info -JSS(transTreeHash); // out: ledger/Ledger.cpp -JSS(transaction); // in: Tx - // out: NetworkOPs, AcceptedLedgerTx, -JSS(transaction_hash); // out: RCLCxPeerPos, LedgerToJson -JSS(transactions); // out: LedgerToJson, - // in: AccountTx*, Unsubscribe -JSS(TRANSACTION_RESULTS); // out: RPC server_definitions - // matches definitions.json format -JSS(TRANSACTION_TYPES); // out: RPC server_definitions - // matches definitions.json format -JSS(TRANSACTION_FLAGS); // out: RPC server_definitions -JSS(TRANSACTION_FORMATS); // out: RPC server_definitions -JSS(TYPES); // out: RPC server_definitions - // matches definitions.json format -JSS(transfer_rate); // out: nft_info (clio) -JSS(transitions); // out: NetworkOPs -JSS(treenode_cache_size); // out: GetCounts -JSS(treenode_track_size); // out: GetCounts -JSS(trusted); // out: UnlList -JSS(trusted_validator_keys); // out: ValidatorList -JSS(tx); // out: STTx, AccountTx* -JSS(tx_blob); // in/out: Submit, - // in: TransactionSign, AccountTx* -JSS(tx_hash); // in: TransactionEntry -JSS(tx_json); // in/out: TransactionSign - // out: TransactionEntry -JSS(tx_signing_hash); // out: TransactionSign -JSS(tx_unsigned); // out: TransactionSign -JSS(txn_count); // out: NetworkOPs -JSS(txr_tx_cnt); // out: protocol message tx's count -JSS(txr_tx_sz); // out: protocol message tx's size -JSS(txr_have_txs_cnt); // out: protocol message have tx count -JSS(txr_have_txs_sz); // out: protocol message have tx size -JSS(txr_get_ledger_cnt); // out: protocol message get ledger count -JSS(txr_get_ledger_sz); // out: protocol message get ledger size -JSS(txr_ledger_data_cnt); // out: protocol message ledger data count -JSS(txr_ledger_data_sz); // out: protocol message ledger data size -JSS(txr_transactions_cnt); // out: protocol message get object count -JSS(txr_transactions_sz); // out: protocol message get object size -JSS(txr_selected_cnt); // out: selected peers count -JSS(txr_suppressed_cnt); // out: suppressed peers count -JSS(txr_not_enabled_cnt); // out: peers with tx reduce-relay disabled count -JSS(txr_missing_tx_freq); // out: missing tx frequency average -JSS(txs); // out: TxHistory -JSS(type); // in: AccountObjects - // out: NetworkOPs, RPC server_definitions - // OverlayImpl, Logic -JSS(type_hex); // out: STPathSet -JSS(unl); // out: UnlList -JSS(unlimited); // out: Connection.h -JSS(uptime); // out: GetCounts -JSS(uri); // out: ValidatorSites -JSS(url); // in/out: Subscribe, Unsubscribe -JSS(url_password); // in: Subscribe -JSS(url_username); // in: Subscribe -JSS(urlgravatar); // -JSS(username); // in: Subscribe -JSS(validated); // out: NetworkOPs, RPCHelpers, AccountTx* - // Tx -JSS(validator_list_expires); // out: NetworkOps, ValidatorList -JSS(validator_list); // out: NetworkOps, ValidatorList -JSS(validators); -JSS(validated_hash); // out: NetworkOPs -JSS(validated_ledger); // out: NetworkOPs -JSS(validated_ledger_index); // out: SubmitTransaction -JSS(validated_ledgers); // out: NetworkOPs -JSS(validation_key); // out: ValidationCreate, ValidationSeed -JSS(validation_private_key); // out: ValidationCreate -JSS(validation_public_key); // out: ValidationCreate, ValidationSeed -JSS(validation_quorum); // out: NetworkOPs -JSS(validation_seed); // out: ValidationCreate, ValidationSeed -JSS(validations); // out: AmendmentTableImpl -JSS(validator_list_threshold); // out: ValidatorList -JSS(validator_sites); // out: ValidatorSites -JSS(value); // out: STAmount -JSS(vault_id); // in: VaultInfo -JSS(version); // out: RPCVersion -JSS(vetoed); // out: AmendmentTableImpl -JSS(volume_a); // out: BookChanges -JSS(volume_b); // out: BookChanges -JSS(vote); // in: Feature -JSS(vote_slots); // out: amm_info -JSS(vote_weight); // out: amm_info -JSS(warning); // rpc: -JSS(warnings); // out: server_info, server_state -JSS(workers); -JSS(write_load); // out: GetCounts -// clang-format on +JSS(account_history_tx_first); // out: Account txn history subscribe +JSS(account_history_boundary); // out: Account txn history subscribe +JSS(accounts); // in: LedgerEntry, Subscribe, handlers/Ledger, Unsubscribe +JSS(accounts_proposed); // in: Subscribe, Unsubscribe +JSS(action); // +JSS(active); // out: OverlayImpl +JSS(acquiring); // out: LedgerRequest +JSS(address); // out: PeerImp +JSS(affected); // out: AcceptedLedgerTx +JSS(age); // out: NetworkOPs, Peers +JSS(alternatives); // out: PathRequest, RipplePathFind +JSS(amendment_blocked); // out: NetworkOPs +JSS(amm_account); // in: amm_info +JSS(amount); // out: AccountChannels, amm_info +JSS(amount2); // out: amm_info +JSS(api_version); // in: many, out: Version +JSS(api_version_low); // out: Version +JSS(applied); // out: SubmitTransaction +JSS(asks); // out: Subscribe +JSS(asset); // in: amm_info +JSS(asset2); // in: amm_info +JSS(assets); // out: GatewayBalances +JSS(asset_frozen); // out: amm_info +JSS(asset2_frozen); // out: amm_info +JSS(attestations); // +JSS(attestation_reward_account); // +JSS(auction_slot); // out: amm_info +JSS(authorized); // out: AccountLines +JSS(authorize); // out: delegate +JSS(authorized_credentials); // in: ledger_entry DepositPreauth +JSS(auth_accounts); // out: amm_info +JSS(auth_change); // out: AccountInfo +JSS(auth_change_queued); // out: AccountInfo +JSS(available); // out: ValidatorList +JSS(avg_bps_recv); // out: Peers +JSS(avg_bps_sent); // out: Peers +JSS(balance); // out: AccountLines +JSS(balances); // out: GatewayBalances +JSS(base); // out: LogLevel +JSS(base_asset); // in: get_aggregate_price +JSS(base_fee); // out: NetworkOPs +JSS(base_fee_xrp); // out: NetworkOPs +JSS(bids); // out: Subscribe +JSS(binary); // in: AccountTX, LedgerEntry, AccountTxOld, Tx LedgerData +JSS(blob); // out: ValidatorList +JSS(blobs_v2); // out: ValidatorList + // in: UNL +JSS(books); // in: Subscribe, Unsubscribe +JSS(both); // in: Subscribe, Unsubscribe +JSS(both_sides); // in: Subscribe, Unsubscribe +JSS(branch); // out: server_info +JSS(broadcast); // out: SubmitTransaction +JSS(bridge_account); // in: LedgerEntry +JSS(build_path); // in: TransactionSign +JSS(build_version); // out: NetworkOPs +JSS(cancel_after); // out: AccountChannels +JSS(can_delete); // out: CanDelete +JSS(mpt_amount); // out: mpt_holders +JSS(mpt_issuance_id); // in: Payment, mpt_holders +JSS(mptoken_index); // out: mpt_holders +JSS(changes); // out: BookChanges +JSS(channel_id); // out: AccountChannels +JSS(channels); // out: AccountChannels +JSS(check_nodes); // in: LedgerCleaner +JSS(clear); // in/out: FetchInfo +JSS(close); // out: BookChanges +JSS(close_flags); // out: LedgerToJson +JSS(close_time); // in: Application, out: NetworkOPs, RCLCxPeerPos, LedgerToJson +JSS(close_time_iso); // out: Tx, NetworkOPs, TransactionEntry AccountTx, LedgerToJson +JSS(close_time_estimated); // in: Application, out: LedgerToJson +JSS(close_time_human); // out: LedgerToJson +JSS(close_time_offset); // out: NetworkOPs +JSS(close_time_resolution); // in: Application; out: LedgerToJson +JSS(closed); // out: NetworkOPs, LedgerToJson, handlers/Ledger +JSS(closed_ledger); // out: NetworkOPs +JSS(cluster); // out: PeerImp +JSS(code); // out: errors +JSS(command); // in: RPCHandler +JSS(common); // out: RPC server_definitions +JSS(complete); // out: NetworkOPs, InboundLedger +JSS(complete_ledgers); // out: NetworkOPs, PeerImp +JSS(consensus); // out: NetworkOPs, LedgerConsensus +JSS(converge_time); // out: NetworkOPs +JSS(converge_time_s); // out: NetworkOPs +JSS(cookie); // out: NetworkOPs +JSS(count); // in: AccountTx*, ValidatorList +JSS(counters); // in/out: retrieve counters +JSS(credentials); // in: deposit_authorized +JSS(credential_type); // in: LedgerEntry DepositPreauth +JSS(ctid); // in/out: Tx RPC +JSS(currency_a); // out: BookChanges +JSS(currency_b); // out: BookChanges +JSS(currency); // in: paths/PathRequest, STAmount + // out: STPathSet, STAmount, AccountLines +JSS(current); // out: OwnerInfo +JSS(current_activities); // +JSS(current_ledger_size); // out: TxQ +JSS(current_queue_size); // out: TxQ +JSS(data); // out: LedgerData +JSS(date); // out: tx/Transaction, NetworkOPs +JSS(dbKBLedger); // out: getCounts +JSS(dbKBTotal); // out: getCounts +JSS(dbKBTransaction); // out: getCounts +JSS(debug_signing); // in: TransactionSign +JSS(deletion_blockers_only); // in: AccountObjects +JSS(delivered_amount); // out: insertDeliveredAmount +JSS(deposit_authorized); // out: deposit_authorized +JSS(deprecated); // +JSS(descending); // in: AccountTx* +JSS(description); // in/out: Reservations +JSS(destination); // in: nft_buy_offers, nft_sell_offers +JSS(destination_account); // in: PathRequest, RipplePathFind, account_lines + // out: AccountChannels +JSS(destination_amount); // in: PathRequest, RipplePathFind +JSS(destination_currencies); // in: PathRequest, RipplePathFind +JSS(destination_tag); // in: PathRequest + // out: AccountChannels +JSS(details); // out: Manifest, server_info +JSS(dir_entry); // out: DirectoryEntryIterator +JSS(dir_index); // out: DirectoryEntryIterator +JSS(dir_root); // out: DirectoryEntryIterator +JSS(discounted_fee); // out: amm_info +JSS(domain); // out: ValidatorInfo, Manifest +JSS(drops); // out: TxQ +JSS(duration_us); // out: NetworkOPs +JSS(effective); // out: ValidatorList + // in: UNL +JSS(enabled); // out: AmendmentTable +JSS(engine_result); // out: NetworkOPs, TransactionSign, Submit +JSS(engine_result_code); // out: NetworkOPs, TransactionSign, Submit +JSS(engine_result_message); // out: NetworkOPs, TransactionSign, Submit +JSS(entire_set); // out: get_aggregate_price +JSS(ephemeral_key); // out: ValidatorInfo + // in/out: Manifest +JSS(error); // out: error +JSS(errored); // +JSS(error_code); // out: error +JSS(error_exception); // out: Submit +JSS(error_message); // out: error +JSS(expand); // in: handler/Ledger +JSS(expected_date); // out: any (warnings) +JSS(expected_date_UTC); // out: any (warnings) +JSS(expected_ledger_size); // out: TxQ +JSS(expiration); // out: AccountOffers, AccountChannels, ValidatorList, amm_info +JSS(fail_hard); // in: Sign, Submit +JSS(failed); // out: InboundLedger +JSS(feature); // in: Feature +JSS(features); // out: Feature +JSS(fee_base); // out: NetworkOPs +JSS(fee_div_max); // in: TransactionSign +JSS(fee_level); // out: AccountInfo +JSS(fee_mult_max); // in: TransactionSign +JSS(fee_ref); // out: NetworkOPs, DEPRECATED +JSS(fetch_pack); // out: NetworkOPs +JSS(FIELDS); // out: RPC server_definitions + // matches definitions.json format +JSS(first); // out: rpc/Version +JSS(finished); // +JSS(fix_txns); // in: LedgerCleaner +JSS(flags); // out: AccountOffers, NetworkOPs +JSS(forward); // in: AccountTx +JSS(freeze); // out: AccountLines +JSS(freeze_peer); // out: AccountLines +JSS(deep_freeze); // out: AccountLines +JSS(deep_freeze_peer); // out: AccountLines +JSS(frozen_balances); // out: GatewayBalances +JSS(full); // in: LedgerClearer, handlers/Ledger +JSS(full_reply); // out: PathFind +JSS(fullbelow_size); // out: GetCounts +JSS(git); // out: server_info +JSS(good); // out: RPCVersion +JSS(hash); // out: NetworkOPs, InboundLedger, LedgerToJson, STTx; field +JSS(have_header); // out: InboundLedger +JSS(have_state); // out: InboundLedger +JSS(have_transactions); // out: InboundLedger +JSS(high); // out: BookChanges +JSS(highest_sequence); // out: AccountInfo +JSS(highest_ticket); // out: AccountInfo +JSS(historical_perminute); // historical_perminute. +JSS(holders); // out: MPTHolders +JSS(hostid); // out: NetworkOPs +JSS(hotwallet); // in: GatewayBalances +JSS(id); // websocket. +JSS(ident); // in: AccountCurrencies, AccountInfo, OwnerInfo +JSS(ignore_default); // in: AccountLines +JSS(in); // out: OverlayImpl +JSS(inLedger); // out: tx/Transaction +JSS(inbound); // out: PeerImp +JSS(index); // in: LedgerEntry + // out: STLedgerEntry, LedgerEntry, TxHistory, LedgerData +JSS(info); // out: ServerInfo, ConsensusInfo, FetchInfo +JSS(initial_sync_duration_us); // +JSS(internal_command); // in: Internal +JSS(invalid_API_version); // out: Many, when a request has an invalid version +JSS(io_latency_ms); // out: NetworkOPs +JSS(ip); // in: Connect, out: OverlayImpl +JSS(is_burned); // out: nft_info (clio) +JSS(isSerialized); // out: RPC server_definitions + // matches definitions.json format +JSS(isSigningField); // out: RPC server_definitions + // matches definitions.json format +JSS(isVLEncoded); // out: RPC server_definitions + // matches definitions.json format +JSS(issuer); // in: RipplePathFind, Subscribe, Unsubscribe, BookOffers + // out: STPathSet, STAmount +JSS(job); // +JSS(job_queue); // +JSS(jobs); // +JSS(jsonrpc); // json version +JSS(jq_trans_overflow); // JobQueue transaction limit overflow. +JSS(kept); // out: SubmitTransaction +JSS(key); // out +JSS(key_type); // in/out: WalletPropose, TransactionSign +JSS(latency); // out: PeerImp +JSS(last); // out: RPCVersion +JSS(last_close); // out: NetworkOPs +JSS(last_refresh_time); // out: ValidatorSite +JSS(last_refresh_status); // out: ValidatorSite +JSS(last_refresh_message); // out: ValidatorSite +JSS(ledger); // in: NetworkOPs, LedgerCleaner, RPCHelpers + // out: NetworkOPs, PeerImp +JSS(ledger_current_index); // out: NetworkOPs, RPCHelpers, LedgerCurrent, LedgerAccept, + // AccountLines +JSS(ledger_data); // out: LedgerHeader +JSS(ledger_hash); // in: RPCHelpers, LedgerRequest, RipplePathFind, + // TransactionEntry, handlers/Ledger + // out: NetworkOPs, RPCHelpers, LedgerClosed, LedgerData, + // AccountLines +JSS(ledger_hit_rate); // out: GetCounts +JSS(ledger_index); // in/out: many +JSS(ledger_index_max); // in, out: AccountTx* +JSS(ledger_index_min); // in, out: AccountTx* +JSS(ledger_max); // in, out: AccountTx* +JSS(ledger_min); // in, out: AccountTx* +JSS(ledger_time); // out: NetworkOPs +JSS(LEDGER_ENTRY_TYPES); // out: RPC server_definitions + // matches definitions.json format +JSS(LEDGER_ENTRY_FLAGS); // out: RPC server_definitions +JSS(LEDGER_ENTRY_FORMATS); // out: RPC server_definitions +JSS(levels); // LogLevels +JSS(limit); // in/out: AccountTx*, AccountOffers, AccountLines, AccountObjects + // in: LedgerData, BookOffers +JSS(limit_peer); // out: AccountLines +JSS(lines); // out: AccountLines +JSS(list); // out: ValidatorList +JSS(load); // out: NetworkOPs, PeerImp +JSS(load_base); // out: NetworkOPs +JSS(load_factor); // out: NetworkOPs +JSS(load_factor_cluster); // out: NetworkOPs +JSS(load_factor_fee_escalation); // out: NetworkOPs +JSS(load_factor_fee_queue); // out: NetworkOPs +JSS(load_factor_fee_reference); // out: NetworkOPs +JSS(load_factor_local); // out: NetworkOPs +JSS(load_factor_net); // out: NetworkOPs +JSS(load_factor_server); // out: NetworkOPs +JSS(load_fee); // out: LoadFeeTrackImp, NetworkOPs +JSS(loan_broker_id); // in: LedgerEntry +JSS(loan_seq); // in: LedgerEntry +JSS(local); // out: resource/Logic.h +JSS(local_txs); // out: GetCounts +JSS(local_static_keys); // out: ValidatorList +JSS(locked); // out: GatewayBalances +JSS(low); // out: BookChanges +JSS(lowest_sequence); // out: AccountInfo +JSS(lowest_ticket); // out: AccountInfo +JSS(lp_token); // out: amm_info +JSS(majority); // out: RPC feature +JSS(manifest); // out: ValidatorInfo, Manifest +JSS(marker); // in/out: AccountTx, AccountOffers, AccountLines, AccountObjects, + // LedgerData + // in: BookOffers +JSS(master_key); // out: WalletPropose, NetworkOPs, ValidatorInfo + // in/out: Manifest +JSS(master_seed); // out: WalletPropose +JSS(master_seed_hex); // out: WalletPropose +JSS(master_signature); // out: pubManifest +JSS(max_ledger); // in/out: LedgerCleaner +JSS(max_queue_size); // out: TxQ +JSS(max_spend_drops); // out: AccountInfo +JSS(max_spend_drops_total); // out: AccountInfo +JSS(mean); // out: get_aggregate_price +JSS(median); // out: get_aggregate_price +JSS(median_fee); // out: TxQ +JSS(median_level); // out: TxQ +JSS(message); // error. +JSS(meta); // out: NetworkOPs, AccountTx*, Tx +JSS(meta_blob); // out: NetworkOPs, AccountTx*, Tx +JSS(metaData); // +JSS(metadata); // out: TransactionEntry +JSS(method); // RPC +JSS(methods); // +JSS(metrics); // out: Peers +JSS(min_count); // in: GetCounts +JSS(min_ledger); // in: LedgerCleaner +JSS(minimum_fee); // out: TxQ +JSS(minimum_level); // out: TxQ +JSS(missingCommand); // error +JSS(name); // out: AmendmentTableImpl, PeerImp +JSS(needed_state_hashes); // out: InboundLedger +JSS(needed_transaction_hashes); // out: InboundLedger +JSS(network_id); // out: NetworkOPs +JSS(network_ledger); // out: NetworkOPs +JSS(next_refresh_time); // out: ValidatorSite +JSS(nft_id); // in: nft_sell_offers, nft_buy_offers +JSS(nft_offer_index); // out nft_buy_offers, nft_sell_offers +JSS(nft_serial); // out: account_nfts +JSS(nft_taxon); // out: nft_info (clio) +JSS(nftoken_id); // out: insertNFTokenID +JSS(nftoken_ids); // out: insertNFTokenID +JSS(no_ripple); // out: AccountLines +JSS(no_ripple_peer); // out: AccountLines +JSS(node); // out: LedgerEntry +JSS(node_binary); // out: LedgerEntry +JSS(node_read_bytes); // out: GetCounts +JSS(node_read_errors); // out: GetCounts +JSS(node_read_retries); // out: GetCounts +JSS(node_reads_hit); // out: GetCounts +JSS(node_reads_total); // out: GetCounts +JSS(node_reads_duration_us); // out: GetCounts +JSS(node_size); // out: server_info +JSS(nodes); // out: VaultInfo +JSS(nodestore); // out: GetCounts +JSS(node_writes); // out: GetCounts +JSS(node_written_bytes); // out: GetCounts +JSS(node_writes_duration_us); // out: GetCounts +JSS(node_write_retries); // out: GetCounts +JSS(node_writes_delayed); // out::GetCounts +JSS(nth); // out: RPC server_definitions +JSS(obligations); // out: GatewayBalances +JSS(offers); // out: NetworkOPs, AccountOffers, Subscribe +JSS(offer_id); // out: insertNFTokenOfferID +JSS(offline); // in: TransactionSign +JSS(offset); // in/out: AccountTxOld +JSS(open); // out: handlers/Ledger +JSS(open_ledger_cost); // out: SubmitTransaction +JSS(open_ledger_fee); // out: TxQ +JSS(open_ledger_level); // out: TxQ +JSS(optionality); // out: server_definitions +JSS(oracles); // in: get_aggregate_price +JSS(oracle_document_id); // in: get_aggregate_price +JSS(out); // out: OverlayImpl +JSS(owner); // in: LedgerEntry, out: NetworkOPs +JSS(owner_funds); // in/out: Ledger, NetworkOPs, AcceptedLedgerTx +JSS(page_index); // +JSS(params); // RPC +JSS(parent_close_time); // out: LedgerToJson +JSS(parent_hash); // out: LedgerToJson +JSS(partition); // in: LogLevel +JSS(passphrase); // in: WalletPropose +JSS(password); // in: Subscribe +JSS(paths); // in: RipplePathFind +JSS(paths_canonical); // out: RipplePathFind +JSS(paths_computed); // out: PathRequest, RipplePathFind +JSS(peer); // in: AccountLines +JSS(peer_authorized); // out: AccountLines +JSS(peer_id); // out: RCLCxPeerPos +JSS(peers); // out: InboundLedger, handlers/Peers, Overlay +JSS(peer_disconnects); // Severed peer connection counter. +JSS(peer_disconnects_resources); // Severed peer connections because of + // excess resource consumption. +JSS(port); // in: Connect, out: NetworkOPs +JSS(ports); // out: NetworkOPs +JSS(previous); // out: Reservations +JSS(previous_ledger); // out: LedgerPropose +JSS(price); // out: amm_info, AuctionSlot +JSS(proof); // in: BookOffers +JSS(propose_seq); // out: LedgerPropose +JSS(proposers); // out: NetworkOPs, LedgerConsensus +JSS(protocol); // out: NetworkOPs, PeerImp +JSS(proxied); // out: RPC ping +JSS(pseudo_account); // out: AccountInfo +JSS(pubkey_node); // out: NetworkOPs +JSS(pubkey_publisher); // out: ValidatorList +JSS(pubkey_validator); // out: NetworkOPs, ValidatorList +JSS(public_key); // out: OverlayImpl, PeerImp, WalletPropose, ValidatorInfo + // in/out: Manifest +JSS(public_key_hex); // out: WalletPropose +JSS(published_ledger); // out: NetworkOPs +JSS(publisher_lists); // out: ValidatorList +JSS(quality); // out: NetworkOPs +JSS(quality_in); // out: AccountLines +JSS(quality_out); // out: AccountLines +JSS(queue); // in: AccountInfo +JSS(queue_data); // out: AccountInfo +JSS(queued); // out: SubmitTransaction +JSS(queued_duration_us); // +JSS(quote_asset); // in: get_aggregate_price +JSS(random); // out: Random +JSS(raw_meta); // out: AcceptedLedgerTx +JSS(receive_currencies); // out: AccountCurrencies +JSS(reference_level); // out: TxQ +JSS(refresh_interval); // in: UNL +JSS(refresh_interval_min); // out: ValidatorSites +JSS(regular_seed); // in/out: LedgerEntry +JSS(remaining); // out: ValidatorList +JSS(remote); // out: Logic.h +JSS(request); // RPC +JSS(requested); // out: Manifest +JSS(reservations); // out: Reservations +JSS(reserve_base); // out: NetworkOPs +JSS(reserve_base_xrp); // out: NetworkOPs +JSS(reserve_inc); // out: NetworkOPs +JSS(reserve_inc_xrp); // out: NetworkOPs +JSS(response); // websocket +JSS(result); // RPC +JSS(ripple_lines); // out: NetworkOPs +JSS(ripple_state); // in: LedgerEntr +JSS(ripplerpc); // XRPL RPC version +JSS(role); // out: Ping.cpp +JSS(rpc); // +JSS(rt_accounts); // in: Subscribe, Unsubscribe +JSS(running_duration_us); // +JSS(search_depth); // in: RipplePathFind +JSS(searched_all); // out: Tx +JSS(secret); // in: TransactionSign, ValidationCreate, ValidationSeed, + // channel_authorize +JSS(seed); // +JSS(seed_hex); // in: WalletPropose, TransactionSign +JSS(send_currencies); // out: AccountCurrencies +JSS(send_max); // in: PathRequest, RipplePathFind +JSS(seq); // in: LedgerEntry + // out: NetworkOPs, RPCSub, AccountOffers, ValidatorList, + // ValidatorInfo, Manifest +JSS(sequence); // in: UNL +JSS(sequence_count); // out: AccountInfo +JSS(server_domain); // out: NetworkOPs +JSS(server_state); // out: NetworkOPs +JSS(server_state_duration_us); // out: NetworkOPs +JSS(server_status); // out: NetworkOPs +JSS(server_version); // out: NetworkOPs +JSS(settle_delay); // out: AccountChannels +JSS(severity); // in: LogLevel +JSS(shares); // out: VaultInfo +JSS(signature); // out: NetworkOPs, ChannelAuthorize +JSS(signature_target); // in: TransactionSign +JSS(signature_verified); // out: ChannelVerify +JSS(signing_key); // out: NetworkOPs +JSS(signing_keys); // out: ValidatorList +JSS(signing_time); // out: NetworkOPs +JSS(signer_lists); // in/out: AccountInfo +JSS(size); // out: get_aggregate_price +JSS(snapshot); // in: Subscribe +JSS(source_account); // in: PathRequest, RipplePathFind +JSS(source_amount); // in: PathRequest, RipplePathFind +JSS(source_currencies); // in: PathRequest, RipplePathFind +JSS(source_tag); // out: AccountChannels +JSS(stand_alone); // out: NetworkOPs +JSS(standard_deviation); // out: get_aggregate_price +JSS(start); // in: TxHistory +JSS(started); // +JSS(state_accounting); // out: NetworkOPs +JSS(state_now); // in: Subscribe +JSS(status); // error +JSS(stop); // in: LedgerCleaner +JSS(stop_history_tx_only); // in: Unsubscribe, stop history tx stream +JSS(streams); // in: Subscribe, Unsubscribe +JSS(strict); // in: AccountCurrencies, AccountInfo +JSS(sub_index); // in: LedgerEntry +JSS(subcommand); // in: PathFind +JSS(subject); // in: LedgerEntry Credential +JSS(success); // rpc +JSS(supported); // out: AmendmentTableImpl +JSS(sync_mode); // in: Submit +JSS(system_time_offset); // out: NetworkOPs +JSS(tag); // out: Peers +JSS(taker); // in: Subscribe, BookOffers +JSS(taker_gets); // in: Subscribe, Unsubscribe, BookOffers +JSS(taker_gets_funded); // out: NetworkOPs +JSS(taker_pays); // in: Subscribe, Unsubscribe, BookOffers +JSS(taker_pays_funded); // out: NetworkOPs +JSS(threshold); // in: Blacklist +JSS(ticket_count); // out: AccountInfo +JSS(ticket_seq); // in: LedgerEntry +JSS(time); // +JSS(timeouts); // out: InboundLedger +JSS(time_threshold); // in/out: Oracle aggregate +JSS(time_interval); // out: AMM Auction Slot +JSS(track); // out: PeerImp +JSS(traffic); // out: Overlay +JSS(trim); // in: get_aggregate_price +JSS(trimmed_set); // out: get_aggregate_price +JSS(total); // out: counters +JSS(total_bytes_recv); // out: Peers +JSS(total_bytes_sent); // out: Peers +JSS(total_coins); // out: LedgerToJson +JSS(trading_fee); // out: amm_info +JSS(transTreeHash); // out: ledger/Ledger.cpp +JSS(transaction); // in: Tx + // out: NetworkOPs, AcceptedLedgerTx, +JSS(transaction_hash); // out: RCLCxPeerPos, LedgerToJson +JSS(transactions); // out: LedgerToJson, + // in: AccountTx*, Unsubscribe +JSS(TRANSACTION_RESULTS); // out: RPC server_definitions + // matches definitions.json format +JSS(TRANSACTION_TYPES); // out: RPC server_definitions + // matches definitions.json format +JSS(TRANSACTION_FLAGS); // out: RPC server_definitions +JSS(TRANSACTION_FORMATS); // out: RPC server_definitions +JSS(TYPES); // out: RPC server_definitions + // matches definitions.json format +JSS(transfer_rate); // out: nft_info (clio) +JSS(transitions); // out: NetworkOPs +JSS(treenode_cache_size); // out: GetCounts +JSS(treenode_track_size); // out: GetCounts +JSS(trusted); // out: UnlList +JSS(trusted_validator_keys); // out: ValidatorList +JSS(tx); // out: STTx, AccountTx* +JSS(tx_blob); // in/out: Submit, + // in: TransactionSign, AccountTx* +JSS(tx_hash); // in: TransactionEntry +JSS(tx_json); // in/out: TransactionSign + // out: TransactionEntry +JSS(tx_signing_hash); // out: TransactionSign +JSS(tx_unsigned); // out: TransactionSign +JSS(txn_count); // out: NetworkOPs +JSS(txr_tx_cnt); // out: protocol message tx's count +JSS(txr_tx_sz); // out: protocol message tx's size +JSS(txr_have_txs_cnt); // out: protocol message have tx count +JSS(txr_have_txs_sz); // out: protocol message have tx size +JSS(txr_get_ledger_cnt); // out: protocol message get ledger count +JSS(txr_get_ledger_sz); // out: protocol message get ledger size +JSS(txr_ledger_data_cnt); // out: protocol message ledger data count +JSS(txr_ledger_data_sz); // out: protocol message ledger data size +JSS(txr_transactions_cnt); // out: protocol message get object count +JSS(txr_transactions_sz); // out: protocol message get object size +JSS(txr_selected_cnt); // out: selected peers count +JSS(txr_suppressed_cnt); // out: suppressed peers count +JSS(txr_not_enabled_cnt); // out: peers with tx reduce-relay disabled count +JSS(txr_missing_tx_freq); // out: missing tx frequency average +JSS(txs); // out: TxHistory +JSS(type); // in: AccountObjects + // out: NetworkOPs, RPC server_definitions OverlayImpl, Logic +JSS(type_hex); // out: STPathSet +JSS(unl); // out: UnlList +JSS(unlimited); // out: Connection.h +JSS(uptime); // out: GetCounts +JSS(uri); // out: ValidatorSites +JSS(url); // in/out: Subscribe, Unsubscribe +JSS(url_password); // in: Subscribe +JSS(url_username); // in: Subscribe +JSS(urlgravatar); // +JSS(username); // in: Subscribe +JSS(validated); // out: NetworkOPs, RPCHelpers, AccountTx*, Tx +JSS(validator_list_expires); // out: NetworkOps, ValidatorList +JSS(validator_list); // out: NetworkOps, ValidatorList +JSS(validators); // +JSS(validated_hash); // out: NetworkOPs +JSS(validated_ledger); // out: NetworkOPs +JSS(validated_ledger_index); // out: SubmitTransaction +JSS(validated_ledgers); // out: NetworkOPs +JSS(validation_key); // out: ValidationCreate, ValidationSeed +JSS(validation_private_key); // out: ValidationCreate +JSS(validation_public_key); // out: ValidationCreate, ValidationSeed +JSS(validation_quorum); // out: NetworkOPs +JSS(validation_seed); // out: ValidationCreate, ValidationSeed +JSS(validations); // out: AmendmentTableImpl +JSS(validator_list_threshold); // out: ValidatorList +JSS(validator_sites); // out: ValidatorSites +JSS(value); // out: STAmount +JSS(vault_id); // in: VaultInfo +JSS(version); // out: RPCVersion +JSS(vetoed); // out: AmendmentTableImpl +JSS(volume_a); // out: BookChanges +JSS(volume_b); // out: BookChanges +JSS(vote); // in: Feature +JSS(vote_slots); // out: amm_info +JSS(vote_weight); // out: amm_info +JSS(warning); // rpc: +JSS(warnings); // out: server_info, server_state +JSS(workers); // +JSS(write_load); // out: GetCounts #pragma push_macro("TRANSACTION") #undef TRANSACTION diff --git a/include/xrpl/protocol/nft.h b/include/xrpl/protocol/nft.h index 4be58c381c..821d146ff7 100644 --- a/include/xrpl/protocol/nft.h +++ b/include/xrpl/protocol/nft.h @@ -39,7 +39,7 @@ constexpr std::uint16_t const flagMutable = 0x0010; inline std::uint16_t getFlags(uint256 const& id) { - std::uint16_t flags; + std::uint16_t flags = 0; memcpy(&flags, id.begin(), 2); return boost::endian::big_to_native(flags); } @@ -47,7 +47,7 @@ getFlags(uint256 const& id) inline std::uint16_t getTransferFee(uint256 const& id) { - std::uint16_t fee; + std::uint16_t fee = 0; memcpy(&fee, id.begin() + 2, 2); return boost::endian::big_to_native(fee); } @@ -55,7 +55,7 @@ getTransferFee(uint256 const& id) inline std::uint32_t getSerial(uint256 const& id) { - std::uint32_t seq; + std::uint32_t seq = 0; memcpy(&seq, id.begin() + 28, 4); return boost::endian::big_to_native(seq); } @@ -87,7 +87,7 @@ cipheredTaxon(std::uint32_t tokenSeq, Taxon taxon) inline Taxon getTaxon(uint256 const& id) { - std::uint32_t taxon; + std::uint32_t taxon = 0; memcpy(&taxon, id.begin() + 24, 4); taxon = boost::endian::big_to_native(taxon); diff --git a/include/xrpl/protocol_autogen/.clang-tidy b/include/xrpl/protocol_autogen/.clang-tidy new file mode 100644 index 0000000000..fbc003598d --- /dev/null +++ b/include/xrpl/protocol_autogen/.clang-tidy @@ -0,0 +1,3 @@ +# This disables all checks for this directory and its subdirectories +Checks: "-*" +InheritParentConfig: false diff --git a/include/xrpl/protocol_autogen/LedgerEntryBase.h b/include/xrpl/protocol_autogen/LedgerEntryBase.h new file mode 100644 index 0000000000..0c5b367391 --- /dev/null +++ b/include/xrpl/protocol_autogen/LedgerEntryBase.h @@ -0,0 +1,152 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +/** + * @brief Base class for type-safe ledger entry wrappers. + * + * This class provides common functionality for all ledger entry types, + * including access to common fields (sfLedgerIndex, sfLedgerEntryType, sfFlags). + * + * This is an immutable wrapper around SLE (Serialized Ledger Entry). + * Use the corresponding Builder classes to construct new ledger entries. + */ +class LedgerEntryBase +{ +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)) + { + } + + /** + * @brief Validate the ledger entry + * @return true if validation passes, false otherwise + */ + [[nodiscard]] + bool + validate() const + { + if (!sle_->isFieldPresent(sfLedgerEntryType)) + { + return false; // LCOV_EXCL_LINE + } + auto ledgerEntryType = static_cast(sle_->getFieldU16(sfLedgerEntryType)); + return protocol_autogen::validateSTObject( + *sle_, LedgerFormats::getInstance().findByType(ledgerEntryType)->getSOTemplate()); + } + + /** + * @brief Get the ledger entry type. + * @return The type of this ledger entry + */ + [[nodiscard]] + LedgerEntryType + getType() const + { + return sle_->getType(); + } + + /** + * @brief Get the key (index) of this ledger entry. + * + * The key uniquely identifies this ledger entry in the ledger state. + * @return A constant reference to the 256-bit key + */ + [[nodiscard]] + uint256 const& + getKey() const + { + return sle_->key(); + } + + // Common field getters (from LedgerFormats.cpp commonFields) + + /** + * @brief Get the ledger index (sfLedgerIndex). + * + * This field is OPTIONAL and represents the index of the ledger entry. + * @return The ledger index if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getLedgerIndex() const + { + if (sle_->isFieldPresent(sfLedgerIndex)) + { + return sle_->at(sfLedgerIndex); + } + return std::nullopt; + } + + /** + * @brief Check if the ledger entry has a ledger index. + * @return true if sfLedgerIndex is present, false otherwise + */ + [[nodiscard]] + bool + hasLedgerIndex() const + { + return sle_->isFieldPresent(sfLedgerIndex); + } + + /** + * @brief Get the ledger entry type field (sfLedgerEntryType). + * + * This field is REQUIRED for all ledger entries and indicates the type + * of the ledger entry (e.g., AccountRoot, RippleState, Offer, etc.). + * @return The ledger entry type as a 16-bit unsigned integer + */ + [[nodiscard]] + uint16_t + getLedgerEntryType() const + { + return sle_->at(sfLedgerEntryType); + } + + /** + * @brief Get the flags field (sfFlags). + * + * This field is REQUIRED for all ledger entries and contains + * type-specific flags that modify the behavior of the ledger entry. + * @return The flags value as a 32-bit unsigned integer + */ + [[nodiscard]] + std::uint32_t + getFlags() const + { + return sle_->at(sfFlags); + } + + /** + * @brief Get the underlying SLE object. + * + * Provides direct access to the wrapped serialized ledger entry object + * for cases where the type-safe accessors are insufficient. + * @return A constant reference to the underlying SLE object + */ + [[nodiscard]] + std::shared_ptr + getSle() const + { + return sle_; + } + +protected: + /** @brief The underlying serialized ledger entry being wrapped. */ + std::shared_ptr sle_; +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/LedgerEntryBuilderBase.h b/include/xrpl/protocol_autogen/LedgerEntryBuilderBase.h new file mode 100644 index 0000000000..e2f05c31ab --- /dev/null +++ b/include/xrpl/protocol_autogen/LedgerEntryBuilderBase.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl::ledger_entries { + +/** + * Base class for all ledger entry builders. + * Provides common field setters that are available for all ledger entry types. + */ +template +class LedgerEntryBuilderBase +{ +public: + LedgerEntryBuilderBase() = default; + + LedgerEntryBuilderBase( + SF_UINT16::type::value_type ledgerEntryType, + SF_UINT32::type::value_type flags = 0) + { + // Don't call object_.set(soTemplate) - keep object_ as a free object. + // This avoids creating STBase placeholders for soeDEFAULT fields, + // which would cause applyTemplate() to throw "may not be explicitly + // set to default" when building the SLE. + // The SLE constructor will call applyTemplate() which properly + // handles missing fields. + object_[sfLedgerEntryType] = ledgerEntryType; + object_[sfFlags] = flags; + } + + /** + * @brief Validate the ledger entry + * @return true if validation passes, false otherwise + */ + [[nodiscard]] + bool + validate() const + { + if (!object_.isFieldPresent(sfLedgerEntryType)) + { + return false; // LCOV_EXCL_LINE + } + auto ledgerEntryType = static_cast(object_.getFieldU16(sfLedgerEntryType)); + return protocol_autogen::validateSTObject( + object_, LedgerFormats::getInstance().findByType(ledgerEntryType)->getSOTemplate()); + } + + /** + * Set the ledger index. + * @param value Ledger index + * @return Reference to the derived builder for method chaining. + */ + Derived& + setLedgerIndex(uint256 const& value) + { + object_[sfLedgerIndex] = value; + return static_cast(*this); + } + + /** + * Set the flags. + * @param value Flags value + * @return Reference to the derived builder for method chaining. + */ + Derived& + setFlags(uint32_t value) + { + object_.setFieldU32(sfFlags, value); + return static_cast(*this); + } + +protected: + STObject object_{sfLedgerEntry}; +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/README.md b/include/xrpl/protocol_autogen/README.md new file mode 100644 index 0000000000..be90788b05 --- /dev/null +++ b/include/xrpl/protocol_autogen/README.md @@ -0,0 +1,79 @@ +# Protocol Autogen + +This directory contains auto-generated C++ wrapper classes for XRP Ledger protocol types. + +## Generated Files + +The files in this directory are automatically generated at **CMake configure time** from macro definition files: + +- **Transaction classes** (in `transactions/`): Generated from `include/xrpl/protocol/detail/transactions.macro` by `scripts/generate_tx_classes.py` +- **Ledger entry classes** (in `ledger_entries/`): Generated from `include/xrpl/protocol/detail/ledger_entries.macro` by `scripts/generate_ledger_classes.py` + +## Generation Process + +The generation happens automatically when you **configure** the project (not during build). When you run CMake, the system: + +1. Creates a Python virtual environment in the build directory (`codegen_venv`) +2. Installs Python dependencies from `scripts/requirements.txt` into the venv (only if needed) +3. Runs the Python generation scripts using the venv Python interpreter +4. Parses the macro files to extract type definitions +5. Generates type-safe C++ wrapper classes using Mako templates +6. Places the generated headers in this directory + +### When Regeneration Happens + +The code is regenerated when: + +- You run CMake configure for the first time +- The Python virtual environment doesn't exist +- `scripts/requirements.txt` has been modified + +To force regeneration, delete the build directory and reconfigure. + +### Python Dependencies + +The code generation requires the following Python packages (automatically installed): + +- `pcpp` - C preprocessor for Python +- `pyparsing` - Parser combinator library +- `Mako` - Template engine + +These are isolated in a virtual environment and won't affect your system Python installation. + +## Version Control + +The generated `.h` files **are checked into version control**. This means: + +- Developers without Python 3 can still build the project using the committed files +- CI/CD systems don't need to run code generation if files are up to date +- Changes to generated files are visible in code review + +## Modifying Generated Code + +**Do not manually edit generated files.** Any changes will be overwritten the next time CMake configure runs. + +To modify the generated classes: + +- Edit the macro files in `include/xrpl/protocol/detail/` +- Edit the Mako templates in `scripts/templates/` +- Edit the generation scripts in `scripts/` +- Update Python dependencies in `scripts/requirements.txt` +- Run CMake configure to regenerate + +## Adding Common Fields + +If you add a new common field to `TxFormats.cpp` or `LedgerFormats.cpp`, you should also update the corresponding base classes and templates manually: + +Base classes: + +- `TransactionBase.h` - Add getters for new common transaction fields +- `TransactionBuilderBase.h` - Add setters, and if the field is required, add it to the constructor parameters +- `LedgerEntryBase.h` - Add getters for new common ledger entry fields +- `LedgerEntryBuilderBase.h` - Add setters, and if the field is required, add it to the constructor parameters + +Templates (update to pass required common fields to base class constructors): + +- `scripts/templates/Transaction.h.mako` +- `scripts/templates/LedgerEntry.h.mako` + +These files are **not auto-generated** and must be updated by hand. diff --git a/include/xrpl/protocol_autogen/STObjectValidation.h b/include/xrpl/protocol_autogen/STObjectValidation.h new file mode 100644 index 0000000000..cd0633af5d --- /dev/null +++ b/include/xrpl/protocol_autogen/STObjectValidation.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +namespace xrpl::protocol_autogen { + +[[nodiscard]] +inline bool +validateSTObject(STObject const& obj, SOTemplate const& format) +{ + for (auto const& field : format) + { + if (!obj.isFieldPresent(field.sField()) && field.style() == soeREQUIRED) + { + return false; // LCOV_EXCL_LINE + } + + if (field.supportMPT() == soeMPTNotSupported && obj.isFieldPresent(field.sField())) + { + if (field.sField().fieldType == STI_AMOUNT) + { + auto const& amount = obj.getFieldAmount(field.sField()); + + if (amount.asset().holds()) + return false; // LCOV_EXCL_LINE + } + else if (field.sField().fieldType == STI_ISSUE) + { + auto issue = dynamic_cast(obj.peekAtPField(field.sField())); + if (!issue) + return false; // LCOV_EXCL_LINE + + if (issue->holds()) + return false; // LCOV_EXCL_LINE + } + } + } + + return true; +} + +} // namespace xrpl::protocol_autogen diff --git a/include/xrpl/protocol_autogen/TransactionBase.h b/include/xrpl/protocol_autogen/TransactionBase.h new file mode 100644 index 0000000000..161d718c66 --- /dev/null +++ b/include/xrpl/protocol_autogen/TransactionBase.h @@ -0,0 +1,457 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +/** + * @brief Base class for all transaction wrapper types. + * + * Provides type-safe read-only accessors for common transaction fields. + * This is an immutable wrapper around STTx. Use the corresponding Builder classes + * to construct new transactions. + */ +class TransactionBase +{ +public: + /** + * @brief Construct a transaction wrapper from an existing STTx object. + * @param tx The underlying transaction object to wrap + */ + explicit TransactionBase(std::shared_ptr tx) : tx_(std::move(tx)) + { + } + + /** + * @brief Validate the transaction + * @return true if validation passes, false otherwise + */ + [[nodiscard]] + bool + validate(std::string& reason) const + { + if (!protocol_autogen::validateSTObject( + *tx_, TxFormats::getInstance().findByType(tx_->getTxnType())->getSOTemplate())) + { + // LCOV_EXCL_START + reason = "Transaction failed schema validation"; + return false; + // LCOV_EXCL_STOP + } + + // Pseudo transactions are not submitted to the network + if (isPseudoTx(*tx_)) + { + return true; + } + return passesLocalChecks(*tx_, reason); + } + + /** + * @brief Get the transaction type. + * @return The type of this transaction + */ + [[nodiscard]] + xrpl::TxType + getTransactionType() const + { + return tx_->getTxnType(); + } + + /** + * @brief Get the account initiating the transaction (sfAccount). + * + * This field is REQUIRED for all transactions. + * @return The account ID of the transaction sender + */ + [[nodiscard]] + AccountID + getAccount() const + { + return tx_->at(sfAccount); + } + + /** + * @brief Get the sequence number of the transaction (sfSequence). + * + * This field is REQUIRED for all transactions. + * @return The sequence number + */ + [[nodiscard]] + std::uint32_t + getSequence() const + { + return tx_->at(sfSequence); + } + + /** + * @brief Get the transaction fee (sfFee). + * + * This field is REQUIRED for all transactions. + * @return The fee amount + */ + [[nodiscard]] + STAmount + getFee() const + { + return tx_->at(sfFee); + } + + /** + * @brief Get the signing public key (sfSigningPubKey). + * + * This field is REQUIRED for all transactions. + * @return The public key used for signing as a blob + */ + [[nodiscard]] + Blob + getSigningPubKey() const + { + return tx_->getFieldVL(sfSigningPubKey); + } + + /** + * @brief Get the transaction flags (sfFlags). + * + * This field is OPTIONAL. + * @return The flags value if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getFlags() const + { + if (tx_->isFieldPresent(sfFlags)) + return tx_->at(sfFlags); + return std::nullopt; + } + + /** + * @brief Check if the transaction has flags set. + * @return true if sfFlags is present, false otherwise + */ + [[nodiscard]] + bool + hasFlags() const + { + return tx_->isFieldPresent(sfFlags); + } + + /** + * @brief Get the source tag (sfSourceTag). + * + * This field is OPTIONAL and used to identify the source of a payment. + * @return The source tag value if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getSourceTag() const + { + if (tx_->isFieldPresent(sfSourceTag)) + return tx_->at(sfSourceTag); + return std::nullopt; + } + + /** + * @brief Check if the transaction has a source tag. + * @return true if sfSourceTag is present, false otherwise + */ + [[nodiscard]] + bool + hasSourceTag() const + { + return tx_->isFieldPresent(sfSourceTag); + } + + /** + * @brief Get the previous transaction ID (sfPreviousTxnID). + * + * This field is OPTIONAL and used for transaction chaining. + * @return The previous transaction ID if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getPreviousTxnID() const + { + if (tx_->isFieldPresent(sfPreviousTxnID)) + return tx_->at(sfPreviousTxnID); + return std::nullopt; + } + + /** + * @brief Check if the transaction has a previous transaction ID. + * @return true if sfPreviousTxnID is present, false otherwise + */ + [[nodiscard]] + bool + hasPreviousTxnID() const + { + return tx_->isFieldPresent(sfPreviousTxnID); + } + + /** + * @brief Get the last ledger sequence (sfLastLedgerSequence). + * + * This field is OPTIONAL and specifies the latest ledger sequence + * in which this transaction can be included. + * @return The last ledger sequence if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getLastLedgerSequence() const + { + if (tx_->isFieldPresent(sfLastLedgerSequence)) + return tx_->at(sfLastLedgerSequence); + return std::nullopt; + } + + /** + * @brief Check if the transaction has a last ledger sequence. + * @return true if sfLastLedgerSequence is present, false otherwise + */ + [[nodiscard]] + bool + hasLastLedgerSequence() const + { + return tx_->isFieldPresent(sfLastLedgerSequence); + } + + /** + * @brief Get the account transaction ID (sfAccountTxnID). + * + * This field is OPTIONAL and used to track transaction sequences. + * @return The account transaction ID if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getAccountTxnID() const + { + if (tx_->isFieldPresent(sfAccountTxnID)) + return tx_->at(sfAccountTxnID); + return std::nullopt; + } + + /** + * @brief Check if the transaction has an account transaction ID. + * @return true if sfAccountTxnID is present, false otherwise + */ + [[nodiscard]] + bool + hasAccountTxnID() const + { + return tx_->isFieldPresent(sfAccountTxnID); + } + + /** + * @brief Get the operation limit (sfOperationLimit). + * + * This field is OPTIONAL and limits the number of operations in a transaction. + * @return The operation limit if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getOperationLimit() const + { + if (tx_->isFieldPresent(sfOperationLimit)) + return tx_->at(sfOperationLimit); + return std::nullopt; + } + + /** + * @brief Check if the transaction has an operation limit. + * @return true if sfOperationLimit is present, false otherwise + */ + [[nodiscard]] + bool + hasOperationLimit() const + { + return tx_->isFieldPresent(sfOperationLimit); + } + + /** + * @brief Get the memos array (sfMemos). + * + * This field is OPTIONAL and contains arbitrary data attached to the transaction. + * @note This is an untyped field (STArray). + * @return A reference wrapper to the memos array if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional> + getMemos() const + { + if (tx_->isFieldPresent(sfMemos)) + return tx_->getFieldArray(sfMemos); + return std::nullopt; + } + + /** + * @brief Check if the transaction has memos. + * @return true if sfMemos is present, false otherwise + */ + [[nodiscard]] + bool + hasMemos() const + { + return tx_->isFieldPresent(sfMemos); + } + + /** + * @brief Get the ticket sequence (sfTicketSequence). + * + * This field is OPTIONAL and used when consuming a ticket instead of a sequence number. + * @return The ticket sequence if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getTicketSequence() const + { + if (tx_->isFieldPresent(sfTicketSequence)) + return tx_->at(sfTicketSequence); + return std::nullopt; + } + + /** + * @brief Check if the transaction has a ticket sequence. + * @return true if sfTicketSequence is present, false otherwise + */ + [[nodiscard]] + bool + hasTicketSequence() const + { + return tx_->isFieldPresent(sfTicketSequence); + } + + /** + * @brief Get the transaction signature (sfTxnSignature). + * + * This field is OPTIONAL and contains the signature for single-signed transactions. + * @return The transaction signature as a blob if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getTxnSignature() const + { + if (tx_->isFieldPresent(sfTxnSignature)) + return tx_->getFieldVL(sfTxnSignature); + return std::nullopt; + } + + /** + * @brief Check if the transaction has a transaction signature. + * @return true if sfTxnSignature is present, false otherwise + */ + [[nodiscard]] + bool + hasTxnSignature() const + { + return tx_->isFieldPresent(sfTxnSignature); + } + + /** + * @brief Get the signers array (sfSigners). + * + * This field is OPTIONAL and contains the list of signers for multi-signed transactions. + * @note This is an untyped field (STArray). + * @return A reference wrapper to the signers array if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional> + getSigners() const + { + if (tx_->isFieldPresent(sfSigners)) + return tx_->getFieldArray(sfSigners); + return std::nullopt; + } + + /** + * @brief Check if the transaction has signers. + * @return true if sfSigners is present, false otherwise + */ + [[nodiscard]] + bool + hasSigners() const + { + return tx_->isFieldPresent(sfSigners); + } + + /** + * @brief Get the network ID (sfNetworkID). + * + * This field is OPTIONAL and identifies the network this transaction is intended for. + * @return The network ID if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getNetworkID() const + { + if (tx_->isFieldPresent(sfNetworkID)) + return tx_->at(sfNetworkID); + return std::nullopt; + } + + /** + * @brief Check if the transaction has a network ID. + * @return true if sfNetworkID is present, false otherwise + */ + [[nodiscard]] + bool + hasNetworkID() const + { + return tx_->isFieldPresent(sfNetworkID); + } + + /** + * @brief Get the delegate account (sfDelegate). + * + * This field is OPTIONAL and specifies a delegate account for the transaction. + * @return The delegate account ID if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getDelegate() const + { + if (tx_->isFieldPresent(sfDelegate)) + return tx_->at(sfDelegate); + return std::nullopt; + } + + /** + * @brief Check if the transaction has a delegate account. + * @return true if sfDelegate is present, false otherwise + */ + [[nodiscard]] + bool + hasDelegate() const + { + return tx_->isFieldPresent(sfDelegate); + } + + /** + * @brief Get the underlying STTx object. + * + * Provides direct access to the wrapped transaction object for cases + * where the type-safe accessors are insufficient. + * @return A constant reference to the underlying STTx object + */ + [[nodiscard]] + std::shared_ptr + getSTTx() const + { + return tx_; + } + +protected: + /** @brief The underlying transaction object being wrapped. */ + std::shared_ptr tx_; +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/TransactionBuilderBase.h b/include/xrpl/protocol_autogen/TransactionBuilderBase.h new file mode 100644 index 0000000000..3abfd07a1c --- /dev/null +++ b/include/xrpl/protocol_autogen/TransactionBuilderBase.h @@ -0,0 +1,269 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl::transactions { + +/** + * Base class for all transaction builders. + * Provides common field setters that are available for all transaction types. + */ +template +class TransactionBuilderBase +{ +public: + TransactionBuilderBase() = default; + + TransactionBuilderBase( + SF_UINT16::type::value_type transactionType, + SF_ACCOUNT::type::value_type account, + std::optional sequence, + std::optional fee) + { + // Don't call object_.set(soTemplate) - keep object_ as a free object. + // This avoids creating STBase placeholders for soeDEFAULT fields, + // which would cause applyTemplate() to throw "may not be explicitly + // set to default" when building the STTx. + // The STTx constructor will call applyTemplate() which properly + // handles missing fields. + object_[sfTransactionType] = transactionType; + setAccount(account); + + if (sequence) + { + setSequence(*sequence); + } + if (fee) + { + setFee(*fee); + } + } + + /** + * Set the account that is sending the transaction. + * @param value Account address (typically as a string) + * @return Reference to the derived builder for method chaining. + */ + Derived& + setAccount(AccountID const& value) + { + object_[sfAccount] = value; + return static_cast(*this); + } + + /** + * Set the transaction fee. + * @param value Fee in drops (typically as a string or number) + * @return Reference to the derived builder for method chaining. + */ + Derived& + setFee(STAmount const& value) + { + object_[sfFee] = value; + return static_cast(*this); + } + + /** + * Set the sequence number. + * @param value Sequence number + * @return Reference to the derived builder for method chaining. + */ + Derived& + setSequence(std::uint32_t const& value) + { + object_[sfSequence] = value; + return static_cast(*this); + } + + /** + * Set the ticket sequence to use for this transaction. + * When using a ticket, the regular sequence number is set to 0. + * @param value Ticket sequence number + * @return Reference to the derived builder for method chaining. + */ + Derived& + setTicketSequence(std::uint32_t const& value) + { + object_[sfSequence] = 0u; + object_[sfTicketSequence] = value; + return static_cast(*this); + } + + /** + * Set transaction flags. + * @param value Flags value + * @return Reference to the derived builder for method chaining. + */ + Derived& + setFlags(std::uint32_t const& value) + { + object_[sfFlags] = value; + return static_cast(*this); + } + + /** + * Set the source tag. + * @param value Source tag + * @return Reference to the derived builder for method chaining. + */ + Derived& + setSourceTag(std::uint32_t const& value) + { + object_[sfSourceTag] = value; + return static_cast(*this); + } + + /** + * Set the last ledger sequence. + * @param value Last ledger sequence number + * @return Reference to the derived builder for method chaining. + */ + Derived& + setLastLedgerSequence(std::uint32_t const& value) + { + object_[sfLastLedgerSequence] = value; + return static_cast(*this); + } + + /** + * Set the account transaction ID. + * @param value Account transaction ID (typically as a hex string) + * @return Reference to the derived builder for method chaining. + */ + Derived& + setAccountTxnID(uint256 const& value) + { + object_[sfAccountTxnID] = value; + return static_cast(*this); + } + + /** + * Set the previous transaction ID. + * Used for emulate027 compatibility. + * @param value Previous transaction ID + * @return Reference to the derived builder for method chaining. + */ + Derived& + setPreviousTxnID(uint256 const& value) + { + object_[sfPreviousTxnID] = value; + return static_cast(*this); + } + + /** + * Set the operation limit. + * @param value Operation limit + * @return Reference to the derived builder for method chaining. + */ + Derived& + setOperationLimit(std::uint32_t const& value) + { + object_[sfOperationLimit] = value; + return static_cast(*this); + } + + /** + * Set the memos array. + * @param value Array of memo objects + * @return Reference to the derived builder for method chaining. + */ + Derived& + setMemos(STArray const& value) + { + object_.setFieldArray(sfMemos, value); + return static_cast(*this); + } + + /** + * Set the signers array for multi-signing. + * @param value Array of signer objects + * @return Reference to the derived builder for method chaining. + */ + Derived& + setSigners(STArray const& value) + { + object_.setFieldArray(sfSigners, value); + return static_cast(*this); + } + + /** + * Set the network ID. + * @param value Network ID + * @return Reference to the derived builder for method chaining. + */ + Derived& + setNetworkID(std::uint32_t const& value) + { + object_[sfNetworkID] = value; + return static_cast(*this); + } + + /** + * Set the delegate account for delegated transactions. + * @param value Delegate account ID + * @return Reference to the derived builder for method chaining. + */ + Derived& + setDelegate(AccountID const& value) + { + object_[sfDelegate] = value; + return static_cast(*this); + } + + /** + * Get the underlying STObject. + * @return The STObject + */ + STObject const& + getSTObject() const + { + return object_; + } + +protected: + /** + * Sign the transaction with the given keys. + * + * This sets the SigningPubKey field and computes the TxnSignature. + * + * @param publicKey The public key for signing + * @param secretKey The secret key for signing + * @return Reference to the derived builder for method chaining. + */ + Derived& + sign(PublicKey const& publicKey, SecretKey const& secretKey) + { + // Set the signing public key + object_.setFieldVL(sfSigningPubKey, publicKey.slice()); + + // Build the signing data: HashPrefix::txSign + serialized object + // (without signing fields) + Serializer s; + s.add32(HashPrefix::txSign); + object_.addWithoutSigningFields(s); + + // Sign and set the signature + auto const sig = xrpl::sign(publicKey, secretKey, s.slice()); + object_.setFieldVL(sfTxnSignature, sig); + + return static_cast(*this); + } + + STObject object_{sfTransaction}; +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/Utils.h b/include/xrpl/protocol_autogen/Utils.h new file mode 100644 index 0000000000..057c685209 --- /dev/null +++ b/include/xrpl/protocol_autogen/Utils.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace xrpl::protocol_autogen { + +template +using Optional = std::conditional_t< + std::is_reference_v, + std::optional>>, + std::optional>; + +} diff --git a/include/xrpl/protocol_autogen/ledger_entries/AMM.h b/include/xrpl/protocol_autogen/ledger_entries/AMM.h new file mode 100644 index 0000000000..f92f15eaa8 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/AMM.h @@ -0,0 +1,392 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class AMMBuilder; + +/** + * @brief Ledger Entry: AMM + * + * Type: ltAMM (0x0079) + * RPC Name: amm + * + * Immutable wrapper around SLE providing type-safe field access. + * Use AMMBuilder to construct new ledger entries. + */ +class AMM : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltAMM; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for AMM"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfTradingFee (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTradingFee() const + { + if (hasTradingFee()) + return this->sle_->at(sfTradingFee); + return std::nullopt; + } + + /** + * @brief Check if sfTradingFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTradingFee() const + { + return this->sle_->isFieldPresent(sfTradingFee); + } + + /** + * @brief Get sfVoteSlots (soeOPTIONAL) + * @note This is an untyped field (unknown). + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getVoteSlots() const + { + if (this->sle_->isFieldPresent(sfVoteSlots)) + return this->sle_->getFieldArray(sfVoteSlots); + return std::nullopt; + } + + /** + * @brief Check if sfVoteSlots is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasVoteSlots() const + { + return this->sle_->isFieldPresent(sfVoteSlots); + } + + /** + * @brief Get sfAuctionSlot (soeOPTIONAL) + * @note This is an untyped field (unknown). + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional + getAuctionSlot() const + { + if (this->sle_->isFieldPresent(sfAuctionSlot)) + return this->sle_->getFieldObject(sfAuctionSlot); + return std::nullopt; + } + + /** + * @brief Check if sfAuctionSlot is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuctionSlot() const + { + return this->sle_->isFieldPresent(sfAuctionSlot); + } + + /** + * @brief Get sfLPTokenBalance (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getLPTokenBalance() const + { + return this->sle_->at(sfLPTokenBalance); + } + + /** + * @brief Get sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->sle_->at(sfAsset); + } + + /** + * @brief Get sfAsset2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset2() const + { + return this->sle_->at(sfAsset2); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnID() const + { + if (hasPreviousTxnID()) + return this->sle_->at(sfPreviousTxnID); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnID() const + { + return this->sle_->isFieldPresent(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnLgrSeq() const + { + if (hasPreviousTxnLgrSeq()) + return this->sle_->at(sfPreviousTxnLgrSeq); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnLgrSeq is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnLgrSeq() const + { + return this->sle_->isFieldPresent(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for AMM ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class AMMBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new AMMBuilder with required fields. + * @param account The sfAccount field value. + * @param lPTokenBalance The sfLPTokenBalance field value. + * @param asset The sfAsset field value. + * @param asset2 The sfAsset2 field value. + * @param ownerNode The sfOwnerNode field value. + */ + AMMBuilder(std::decay_t const& account,std::decay_t const& lPTokenBalance,std::decay_t const& asset,std::decay_t const& asset2,std::decay_t const& ownerNode) + : LedgerEntryBuilderBase(ltAMM) + { + setAccount(account); + setLPTokenBalance(lPTokenBalance); + setAsset(asset); + setAsset2(asset2); + setOwnerNode(ownerNode); + } + + /** + * @brief Construct a AMMBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + AMMBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltAMM) + { + throw std::runtime_error("Invalid ledger entry type for AMM"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfTradingFee (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setTradingFee(std::decay_t const& value) + { + object_[sfTradingFee] = value; + return *this; + } + + /** + * @brief Set sfVoteSlots (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setVoteSlots(STArray const& value) + { + object_.setFieldArray(sfVoteSlots, value); + return *this; + } + + /** + * @brief Set sfAuctionSlot (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setAuctionSlot(STObject const& value) + { + object_.setFieldObject(sfAuctionSlot, value); + return *this; + } + + /** + * @brief Set sfLPTokenBalance (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setLPTokenBalance(std::decay_t const& value) + { + object_[sfLPTokenBalance] = value; + return *this; + } + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAsset2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setAsset2(std::decay_t const& value) + { + object_[sfAsset2] = STIssue(sfAsset2, value); + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed AMM wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + AMM + build(uint256 const& index) + { + return AMM{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h b/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h new file mode 100644 index 0000000000..15cf14b21a --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h @@ -0,0 +1,834 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class AccountRootBuilder; + +/** + * @brief Ledger Entry: AccountRoot + * + * Type: ltACCOUNT_ROOT (0x0061) + * RPC Name: account + * + * Immutable wrapper around SLE providing type-safe field access. + * Use AccountRootBuilder to construct new ledger entries. + */ +class AccountRoot : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltACCOUNT_ROOT; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for AccountRoot"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSequence() const + { + return this->sle_->at(sfSequence); + } + + /** + * @brief Get sfBalance (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getBalance() const + { + return this->sle_->at(sfBalance); + } + + /** + * @brief Get sfOwnerCount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getOwnerCount() const + { + return this->sle_->at(sfOwnerCount); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfAccountTxnID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAccountTxnID() const + { + if (hasAccountTxnID()) + return this->sle_->at(sfAccountTxnID); + return std::nullopt; + } + + /** + * @brief Check if sfAccountTxnID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAccountTxnID() const + { + return this->sle_->isFieldPresent(sfAccountTxnID); + } + + /** + * @brief Get sfRegularKey (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getRegularKey() const + { + if (hasRegularKey()) + return this->sle_->at(sfRegularKey); + return std::nullopt; + } + + /** + * @brief Check if sfRegularKey is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasRegularKey() const + { + return this->sle_->isFieldPresent(sfRegularKey); + } + + /** + * @brief Get sfEmailHash (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getEmailHash() const + { + if (hasEmailHash()) + return this->sle_->at(sfEmailHash); + return std::nullopt; + } + + /** + * @brief Check if sfEmailHash is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasEmailHash() const + { + return this->sle_->isFieldPresent(sfEmailHash); + } + + /** + * @brief Get sfWalletLocator (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getWalletLocator() const + { + if (hasWalletLocator()) + return this->sle_->at(sfWalletLocator); + return std::nullopt; + } + + /** + * @brief Check if sfWalletLocator is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasWalletLocator() const + { + return this->sle_->isFieldPresent(sfWalletLocator); + } + + /** + * @brief Get sfWalletSize (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getWalletSize() const + { + if (hasWalletSize()) + return this->sle_->at(sfWalletSize); + return std::nullopt; + } + + /** + * @brief Check if sfWalletSize is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasWalletSize() const + { + return this->sle_->isFieldPresent(sfWalletSize); + } + + /** + * @brief Get sfMessageKey (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMessageKey() const + { + if (hasMessageKey()) + return this->sle_->at(sfMessageKey); + return std::nullopt; + } + + /** + * @brief Check if sfMessageKey is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMessageKey() const + { + return this->sle_->isFieldPresent(sfMessageKey); + } + + /** + * @brief Get sfTransferRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTransferRate() const + { + if (hasTransferRate()) + return this->sle_->at(sfTransferRate); + return std::nullopt; + } + + /** + * @brief Check if sfTransferRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTransferRate() const + { + return this->sle_->isFieldPresent(sfTransferRate); + } + + /** + * @brief Get sfDomain (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomain() const + { + if (hasDomain()) + return this->sle_->at(sfDomain); + return std::nullopt; + } + + /** + * @brief Check if sfDomain is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomain() const + { + return this->sle_->isFieldPresent(sfDomain); + } + + /** + * @brief Get sfTickSize (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTickSize() const + { + if (hasTickSize()) + return this->sle_->at(sfTickSize); + return std::nullopt; + } + + /** + * @brief Check if sfTickSize is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTickSize() const + { + return this->sle_->isFieldPresent(sfTickSize); + } + + /** + * @brief Get sfTicketCount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTicketCount() const + { + if (hasTicketCount()) + return this->sle_->at(sfTicketCount); + return std::nullopt; + } + + /** + * @brief Check if sfTicketCount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTicketCount() const + { + return this->sle_->isFieldPresent(sfTicketCount); + } + + /** + * @brief Get sfNFTokenMinter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNFTokenMinter() const + { + if (hasNFTokenMinter()) + return this->sle_->at(sfNFTokenMinter); + return std::nullopt; + } + + /** + * @brief Check if sfNFTokenMinter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNFTokenMinter() const + { + return this->sle_->isFieldPresent(sfNFTokenMinter); + } + + /** + * @brief Get sfMintedNFTokens (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMintedNFTokens() const + { + if (hasMintedNFTokens()) + return this->sle_->at(sfMintedNFTokens); + return std::nullopt; + } + + /** + * @brief Check if sfMintedNFTokens is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMintedNFTokens() const + { + return this->sle_->isFieldPresent(sfMintedNFTokens); + } + + /** + * @brief Get sfBurnedNFTokens (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBurnedNFTokens() const + { + if (hasBurnedNFTokens()) + return this->sle_->at(sfBurnedNFTokens); + return std::nullopt; + } + + /** + * @brief Check if sfBurnedNFTokens is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBurnedNFTokens() const + { + return this->sle_->isFieldPresent(sfBurnedNFTokens); + } + + /** + * @brief Get sfFirstNFTokenSequence (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getFirstNFTokenSequence() const + { + if (hasFirstNFTokenSequence()) + return this->sle_->at(sfFirstNFTokenSequence); + return std::nullopt; + } + + /** + * @brief Check if sfFirstNFTokenSequence is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasFirstNFTokenSequence() const + { + return this->sle_->isFieldPresent(sfFirstNFTokenSequence); + } + + /** + * @brief Get sfAMMID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAMMID() const + { + if (hasAMMID()) + return this->sle_->at(sfAMMID); + return std::nullopt; + } + + /** + * @brief Check if sfAMMID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAMMID() const + { + return this->sle_->isFieldPresent(sfAMMID); + } + + /** + * @brief Get sfVaultID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getVaultID() const + { + if (hasVaultID()) + return this->sle_->at(sfVaultID); + return std::nullopt; + } + + /** + * @brief Check if sfVaultID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasVaultID() const + { + return this->sle_->isFieldPresent(sfVaultID); + } + + /** + * @brief Get sfLoanBrokerID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanBrokerID() const + { + if (hasLoanBrokerID()) + return this->sle_->at(sfLoanBrokerID); + return std::nullopt; + } + + /** + * @brief Check if sfLoanBrokerID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanBrokerID() const + { + return this->sle_->isFieldPresent(sfLoanBrokerID); + } +}; + +/** + * @brief Builder for AccountRoot ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class AccountRootBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new AccountRootBuilder with required fields. + * @param account The sfAccount field value. + * @param sequence The sfSequence field value. + * @param balance The sfBalance field value. + * @param ownerCount The sfOwnerCount field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + AccountRootBuilder(std::decay_t const& account,std::decay_t const& sequence,std::decay_t const& balance,std::decay_t const& ownerCount,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltACCOUNT_ROOT) + { + setAccount(account); + setSequence(sequence); + setBalance(balance); + setOwnerCount(ownerCount); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a AccountRootBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + AccountRootBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltACCOUNT_ROOT) + { + throw std::runtime_error("Invalid ledger entry type for AccountRoot"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfBalance (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setBalance(std::decay_t const& value) + { + object_[sfBalance] = value; + return *this; + } + + /** + * @brief Set sfOwnerCount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setOwnerCount(std::decay_t const& value) + { + object_[sfOwnerCount] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfAccountTxnID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setAccountTxnID(std::decay_t const& value) + { + object_[sfAccountTxnID] = value; + return *this; + } + + /** + * @brief Set sfRegularKey (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setRegularKey(std::decay_t const& value) + { + object_[sfRegularKey] = value; + return *this; + } + + /** + * @brief Set sfEmailHash (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setEmailHash(std::decay_t const& value) + { + object_[sfEmailHash] = value; + return *this; + } + + /** + * @brief Set sfWalletLocator (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setWalletLocator(std::decay_t const& value) + { + object_[sfWalletLocator] = value; + return *this; + } + + /** + * @brief Set sfWalletSize (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setWalletSize(std::decay_t const& value) + { + object_[sfWalletSize] = value; + return *this; + } + + /** + * @brief Set sfMessageKey (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setMessageKey(std::decay_t const& value) + { + object_[sfMessageKey] = value; + return *this; + } + + /** + * @brief Set sfTransferRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setTransferRate(std::decay_t const& value) + { + object_[sfTransferRate] = value; + return *this; + } + + /** + * @brief Set sfDomain (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setDomain(std::decay_t const& value) + { + object_[sfDomain] = value; + return *this; + } + + /** + * @brief Set sfTickSize (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setTickSize(std::decay_t const& value) + { + object_[sfTickSize] = value; + return *this; + } + + /** + * @brief Set sfTicketCount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setTicketCount(std::decay_t const& value) + { + object_[sfTicketCount] = value; + return *this; + } + + /** + * @brief Set sfNFTokenMinter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setNFTokenMinter(std::decay_t const& value) + { + object_[sfNFTokenMinter] = value; + return *this; + } + + /** + * @brief Set sfMintedNFTokens (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setMintedNFTokens(std::decay_t const& value) + { + object_[sfMintedNFTokens] = value; + return *this; + } + + /** + * @brief Set sfBurnedNFTokens (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setBurnedNFTokens(std::decay_t const& value) + { + object_[sfBurnedNFTokens] = value; + return *this; + } + + /** + * @brief Set sfFirstNFTokenSequence (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setFirstNFTokenSequence(std::decay_t const& value) + { + object_[sfFirstNFTokenSequence] = value; + return *this; + } + + /** + * @brief Set sfAMMID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setAMMID(std::decay_t const& value) + { + object_[sfAMMID] = value; + return *this; + } + + /** + * @brief Set sfVaultID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Set sfLoanBrokerID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Build and return the completed AccountRoot wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + AccountRoot + build(uint256 const& index) + { + return AccountRoot{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Amendments.h b/include/xrpl/protocol_autogen/ledger_entries/Amendments.h new file mode 100644 index 0000000000..2b51b83747 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Amendments.h @@ -0,0 +1,236 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class AmendmentsBuilder; + +/** + * @brief Ledger Entry: Amendments + * + * Type: ltAMENDMENTS (0x0066) + * RPC Name: amendments + * + * Immutable wrapper around SLE providing type-safe field access. + * Use AmendmentsBuilder to construct new ledger entries. + */ +class Amendments : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltAMENDMENTS; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Amendments"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAmendments (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmendments() const + { + if (hasAmendments()) + return this->sle_->at(sfAmendments); + return std::nullopt; + } + + /** + * @brief Check if sfAmendments is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmendments() const + { + return this->sle_->isFieldPresent(sfAmendments); + } + + /** + * @brief Get sfMajorities (soeOPTIONAL) + * @note This is an untyped field (unknown). + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getMajorities() const + { + if (this->sle_->isFieldPresent(sfMajorities)) + return this->sle_->getFieldArray(sfMajorities); + return std::nullopt; + } + + /** + * @brief Check if sfMajorities is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMajorities() const + { + return this->sle_->isFieldPresent(sfMajorities); + } + + /** + * @brief Get sfPreviousTxnID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnID() const + { + if (hasPreviousTxnID()) + return this->sle_->at(sfPreviousTxnID); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnID() const + { + return this->sle_->isFieldPresent(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnLgrSeq() const + { + if (hasPreviousTxnLgrSeq()) + return this->sle_->at(sfPreviousTxnLgrSeq); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnLgrSeq is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnLgrSeq() const + { + return this->sle_->isFieldPresent(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for Amendments ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class AmendmentsBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new AmendmentsBuilder with required fields. + */ + AmendmentsBuilder() + : LedgerEntryBuilderBase(ltAMENDMENTS) + { + } + + /** + * @brief Construct a AmendmentsBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + AmendmentsBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltAMENDMENTS) + { + throw std::runtime_error("Invalid ledger entry type for Amendments"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAmendments (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AmendmentsBuilder& + setAmendments(std::decay_t const& value) + { + object_[sfAmendments] = value; + return *this; + } + + /** + * @brief Set sfMajorities (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AmendmentsBuilder& + setMajorities(STArray const& value) + { + object_.setFieldArray(sfMajorities, value); + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AmendmentsBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AmendmentsBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed Amendments wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Amendments + build(uint256 const& index) + { + return Amendments{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Bridge.h b/include/xrpl/protocol_autogen/ledger_entries/Bridge.h new file mode 100644 index 0000000000..3926ff65fa --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Bridge.h @@ -0,0 +1,346 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class BridgeBuilder; + +/** + * @brief Ledger Entry: Bridge + * + * Type: ltBRIDGE (0x0069) + * RPC Name: bridge + * + * Immutable wrapper around SLE providing type-safe field access. + * Use BridgeBuilder to construct new ledger entries. + */ +class Bridge : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltBRIDGE; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Bridge"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfSignatureReward (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSignatureReward() const + { + return this->sle_->at(sfSignatureReward); + } + + /** + * @brief Get sfMinAccountCreateAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMinAccountCreateAmount() const + { + if (hasMinAccountCreateAmount()) + return this->sle_->at(sfMinAccountCreateAmount); + return std::nullopt; + } + + /** + * @brief Check if sfMinAccountCreateAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMinAccountCreateAmount() const + { + return this->sle_->isFieldPresent(sfMinAccountCreateAmount); + } + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->sle_->at(sfXChainBridge); + } + + /** + * @brief Get sfXChainClaimID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainClaimID() const + { + return this->sle_->at(sfXChainClaimID); + } + + /** + * @brief Get sfXChainAccountCreateCount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainAccountCreateCount() const + { + return this->sle_->at(sfXChainAccountCreateCount); + } + + /** + * @brief Get sfXChainAccountClaimCount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainAccountClaimCount() const + { + return this->sle_->at(sfXChainAccountClaimCount); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for Bridge ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class BridgeBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new BridgeBuilder with required fields. + * @param account The sfAccount field value. + * @param signatureReward The sfSignatureReward field value. + * @param xChainBridge The sfXChainBridge field value. + * @param xChainClaimID The sfXChainClaimID field value. + * @param xChainAccountCreateCount The sfXChainAccountCreateCount field value. + * @param xChainAccountClaimCount The sfXChainAccountClaimCount field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + BridgeBuilder(std::decay_t const& account,std::decay_t const& signatureReward,std::decay_t const& xChainBridge,std::decay_t const& xChainClaimID,std::decay_t const& xChainAccountCreateCount,std::decay_t const& xChainAccountClaimCount,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltBRIDGE) + { + setAccount(account); + setSignatureReward(signatureReward); + setXChainBridge(xChainBridge); + setXChainClaimID(xChainClaimID); + setXChainAccountCreateCount(xChainAccountCreateCount); + setXChainAccountClaimCount(xChainAccountClaimCount); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a BridgeBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + BridgeBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltBRIDGE) + { + throw std::runtime_error("Invalid ledger entry type for Bridge"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfSignatureReward (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setSignatureReward(std::decay_t const& value) + { + object_[sfSignatureReward] = value; + return *this; + } + + /** + * @brief Set sfMinAccountCreateAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setMinAccountCreateAmount(std::decay_t const& value) + { + object_[sfMinAccountCreateAmount] = value; + return *this; + } + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfXChainClaimID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setXChainClaimID(std::decay_t const& value) + { + object_[sfXChainClaimID] = value; + return *this; + } + + /** + * @brief Set sfXChainAccountCreateCount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setXChainAccountCreateCount(std::decay_t const& value) + { + object_[sfXChainAccountCreateCount] = value; + return *this; + } + + /** + * @brief Set sfXChainAccountClaimCount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setXChainAccountClaimCount(std::decay_t const& value) + { + object_[sfXChainAccountClaimCount] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed Bridge wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Bridge + build(uint256 const& index) + { + return Bridge{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Check.h b/include/xrpl/protocol_autogen/ledger_entries/Check.h new file mode 100644 index 0000000000..c071186afb --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Check.h @@ -0,0 +1,427 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class CheckBuilder; + +/** + * @brief Ledger Entry: Check + * + * Type: ltCHECK (0x0043) + * RPC Name: check + * + * Immutable wrapper around SLE providing type-safe field access. + * Use CheckBuilder to construct new ledger entries. + */ +class Check : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltCHECK; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Check"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->sle_->at(sfDestination); + } + + /** + * @brief Get sfSendMax (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSendMax() const + { + return this->sle_->at(sfSendMax); + } + + /** + * @brief Get sfSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSequence() const + { + return this->sle_->at(sfSequence); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfDestinationNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getDestinationNode() const + { + return this->sle_->at(sfDestinationNode); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + return this->sle_->at(sfExpiration); + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->sle_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfInvoiceID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getInvoiceID() const + { + if (hasInvoiceID()) + return this->sle_->at(sfInvoiceID); + return std::nullopt; + } + + /** + * @brief Check if sfInvoiceID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasInvoiceID() const + { + return this->sle_->isFieldPresent(sfInvoiceID); + } + + /** + * @brief Get sfSourceTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSourceTag() const + { + if (hasSourceTag()) + return this->sle_->at(sfSourceTag); + return std::nullopt; + } + + /** + * @brief Check if sfSourceTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSourceTag() const + { + return this->sle_->isFieldPresent(sfSourceTag); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationTag() const + { + if (hasDestinationTag()) + return this->sle_->at(sfDestinationTag); + return std::nullopt; + } + + /** + * @brief Check if sfDestinationTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestinationTag() const + { + return this->sle_->isFieldPresent(sfDestinationTag); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for Check ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class CheckBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new CheckBuilder with required fields. + * @param account The sfAccount field value. + * @param destination The sfDestination field value. + * @param sendMax The sfSendMax field value. + * @param sequence The sfSequence field value. + * @param ownerNode The sfOwnerNode field value. + * @param destinationNode The sfDestinationNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + CheckBuilder(std::decay_t const& account,std::decay_t const& destination,std::decay_t const& sendMax,std::decay_t const& sequence,std::decay_t const& ownerNode,std::decay_t const& destinationNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltCHECK) + { + setAccount(account); + setDestination(destination); + setSendMax(sendMax); + setSequence(sequence); + setOwnerNode(ownerNode); + setDestinationNode(destinationNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a CheckBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + CheckBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltCHECK) + { + throw std::runtime_error("Invalid ledger entry type for Check"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfSendMax (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setSendMax(std::decay_t const& value) + { + object_[sfSendMax] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfDestinationNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setDestinationNode(std::decay_t const& value) + { + object_[sfDestinationNode] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfInvoiceID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setInvoiceID(std::decay_t const& value) + { + object_[sfInvoiceID] = value; + return *this; + } + + /** + * @brief Set sfSourceTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setSourceTag(std::decay_t const& value) + { + object_[sfSourceTag] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed Check wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Check + build(uint256 const& index) + { + return Check{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Credential.h b/include/xrpl/protocol_autogen/ledger_entries/Credential.h new file mode 100644 index 0000000000..4c45f80a8d --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Credential.h @@ -0,0 +1,344 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class CredentialBuilder; + +/** + * @brief Ledger Entry: Credential + * + * Type: ltCREDENTIAL (0x0081) + * RPC Name: credential + * + * Immutable wrapper around SLE providing type-safe field access. + * Use CredentialBuilder to construct new ledger entries. + */ +class Credential : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltCREDENTIAL; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Credential"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfSubject (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getSubject() const + { + return this->sle_->at(sfSubject); + } + + /** + * @brief Get sfIssuer (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getIssuer() const + { + return this->sle_->at(sfIssuer); + } + + /** + * @brief Get sfCredentialType (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getCredentialType() const + { + return this->sle_->at(sfCredentialType); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + return this->sle_->at(sfExpiration); + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->sle_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + return this->sle_->at(sfURI); + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->sle_->isFieldPresent(sfURI); + } + + /** + * @brief Get sfIssuerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getIssuerNode() const + { + return this->sle_->at(sfIssuerNode); + } + + /** + * @brief Get sfSubjectNode (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSubjectNode() const + { + if (hasSubjectNode()) + return this->sle_->at(sfSubjectNode); + return std::nullopt; + } + + /** + * @brief Check if sfSubjectNode is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSubjectNode() const + { + return this->sle_->isFieldPresent(sfSubjectNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for Credential ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class CredentialBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new CredentialBuilder with required fields. + * @param subject The sfSubject field value. + * @param issuer The sfIssuer field value. + * @param credentialType The sfCredentialType field value. + * @param issuerNode The sfIssuerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + CredentialBuilder(std::decay_t const& subject,std::decay_t const& issuer,std::decay_t const& credentialType,std::decay_t const& issuerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltCREDENTIAL) + { + setSubject(subject); + setIssuer(issuer); + setCredentialType(credentialType); + setIssuerNode(issuerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a CredentialBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + CredentialBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltCREDENTIAL) + { + throw std::runtime_error("Invalid ledger entry type for Credential"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfSubject (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setSubject(std::decay_t const& value) + { + object_[sfSubject] = value; + return *this; + } + + /** + * @brief Set sfIssuer (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setIssuer(std::decay_t const& value) + { + object_[sfIssuer] = value; + return *this; + } + + /** + * @brief Set sfCredentialType (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setCredentialType(std::decay_t const& value) + { + object_[sfCredentialType] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Set sfIssuerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setIssuerNode(std::decay_t const& value) + { + object_[sfIssuerNode] = value; + return *this; + } + + /** + * @brief Set sfSubjectNode (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setSubjectNode(std::decay_t const& value) + { + object_[sfSubjectNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed Credential wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Credential + build(uint256 const& index) + { + return Credential{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/DID.h b/include/xrpl/protocol_autogen/ledger_entries/DID.h new file mode 100644 index 0000000000..fc88e988df --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/DID.h @@ -0,0 +1,296 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class DIDBuilder; + +/** + * @brief Ledger Entry: DID + * + * Type: ltDID (0x0049) + * RPC Name: did + * + * Immutable wrapper around SLE providing type-safe field access. + * Use DIDBuilder to construct new ledger entries. + */ +class DID : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltDID; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for DID"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfDIDDocument (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDIDDocument() const + { + if (hasDIDDocument()) + return this->sle_->at(sfDIDDocument); + return std::nullopt; + } + + /** + * @brief Check if sfDIDDocument is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDIDDocument() const + { + return this->sle_->isFieldPresent(sfDIDDocument); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + return this->sle_->at(sfURI); + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->sle_->isFieldPresent(sfURI); + } + + /** + * @brief Get sfData (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getData() const + { + if (hasData()) + return this->sle_->at(sfData); + return std::nullopt; + } + + /** + * @brief Check if sfData is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasData() const + { + return this->sle_->isFieldPresent(sfData); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for DID ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class DIDBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new DIDBuilder with required fields. + * @param account The sfAccount field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + DIDBuilder(std::decay_t const& account,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltDID) + { + setAccount(account); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a DIDBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + DIDBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltDID) + { + throw std::runtime_error("Invalid ledger entry type for DID"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DIDBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfDIDDocument (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DIDBuilder& + setDIDDocument(std::decay_t const& value) + { + object_[sfDIDDocument] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DIDBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Set sfData (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DIDBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DIDBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DIDBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DIDBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed DID wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + DID + build(uint256 const& index) + { + return DID{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Delegate.h b/include/xrpl/protocol_autogen/ledger_entries/Delegate.h new file mode 100644 index 0000000000..4d937eb99a --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Delegate.h @@ -0,0 +1,240 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class DelegateBuilder; + +/** + * @brief Ledger Entry: Delegate + * + * Type: ltDELEGATE (0x0083) + * RPC Name: delegate + * + * Immutable wrapper around SLE providing type-safe field access. + * Use DelegateBuilder to construct new ledger entries. + */ +class Delegate : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltDELEGATE; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Delegate"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfAuthorize (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAuthorize() const + { + return this->sle_->at(sfAuthorize); + } + + /** + * @brief Get sfPermissions (soeREQUIRED) + * @note This is an untyped field (unknown). + * @return The field value. + */ + [[nodiscard]] + STArray const& + getPermissions() const + { + return this->sle_->getFieldArray(sfPermissions); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for Delegate ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class DelegateBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new DelegateBuilder with required fields. + * @param account The sfAccount field value. + * @param authorize The sfAuthorize field value. + * @param permissions The sfPermissions field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + DelegateBuilder(std::decay_t const& account,std::decay_t const& authorize,STArray const& permissions,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltDELEGATE) + { + setAccount(account); + setAuthorize(authorize); + setPermissions(permissions); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a DelegateBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + DelegateBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltDELEGATE) + { + throw std::runtime_error("Invalid ledger entry type for Delegate"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfAuthorize (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateBuilder& + setAuthorize(std::decay_t const& value) + { + object_[sfAuthorize] = value; + return *this; + } + + /** + * @brief Set sfPermissions (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateBuilder& + setPermissions(STArray const& value) + { + object_.setFieldArray(sfPermissions, value); + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed Delegate wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Delegate + build(uint256 const& index) + { + return Delegate{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h b/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h new file mode 100644 index 0000000000..533caba619 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h @@ -0,0 +1,262 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class DepositPreauthBuilder; + +/** + * @brief Ledger Entry: DepositPreauth + * + * Type: ltDEPOSIT_PREAUTH (0x0070) + * RPC Name: deposit_preauth + * + * Immutable wrapper around SLE providing type-safe field access. + * Use DepositPreauthBuilder to construct new ledger entries. + */ +class DepositPreauth : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltDEPOSIT_PREAUTH; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for DepositPreauth"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfAuthorize (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAuthorize() const + { + if (hasAuthorize()) + return this->sle_->at(sfAuthorize); + return std::nullopt; + } + + /** + * @brief Check if sfAuthorize is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuthorize() const + { + return this->sle_->isFieldPresent(sfAuthorize); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfAuthorizeCredentials (soeOPTIONAL) + * @note This is an untyped field (unknown). + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getAuthorizeCredentials() const + { + if (this->sle_->isFieldPresent(sfAuthorizeCredentials)) + return this->sle_->getFieldArray(sfAuthorizeCredentials); + return std::nullopt; + } + + /** + * @brief Check if sfAuthorizeCredentials is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuthorizeCredentials() const + { + return this->sle_->isFieldPresent(sfAuthorizeCredentials); + } +}; + +/** + * @brief Builder for DepositPreauth ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class DepositPreauthBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new DepositPreauthBuilder with required fields. + * @param account The sfAccount field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + DepositPreauthBuilder(std::decay_t const& account,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltDEPOSIT_PREAUTH) + { + setAccount(account); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a DepositPreauthBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + DepositPreauthBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltDEPOSIT_PREAUTH) + { + throw std::runtime_error("Invalid ledger entry type for DepositPreauth"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfAuthorize (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setAuthorize(std::decay_t const& value) + { + object_[sfAuthorize] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfAuthorizeCredentials (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setAuthorizeCredentials(STArray const& value) + { + object_.setFieldArray(sfAuthorizeCredentials, value); + return *this; + } + + /** + * @brief Build and return the completed DepositPreauth wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + DepositPreauth + build(uint256 const& index) + { + return DepositPreauth{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h b/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h new file mode 100644 index 0000000000..545542ec6c --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h @@ -0,0 +1,563 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class DirectoryNodeBuilder; + +/** + * @brief Ledger Entry: DirectoryNode + * + * Type: ltDIR_NODE (0x0064) + * RPC Name: directory + * + * Immutable wrapper around SLE providing type-safe field access. + * Use DirectoryNodeBuilder to construct new ledger entries. + */ +class DirectoryNode : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltDIR_NODE; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for DirectoryNode"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfOwner (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOwner() const + { + if (hasOwner()) + return this->sle_->at(sfOwner); + return std::nullopt; + } + + /** + * @brief Check if sfOwner is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOwner() const + { + return this->sle_->isFieldPresent(sfOwner); + } + + /** + * @brief Get sfTakerPaysCurrency (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTakerPaysCurrency() const + { + if (hasTakerPaysCurrency()) + return this->sle_->at(sfTakerPaysCurrency); + return std::nullopt; + } + + /** + * @brief Check if sfTakerPaysCurrency is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTakerPaysCurrency() const + { + return this->sle_->isFieldPresent(sfTakerPaysCurrency); + } + + /** + * @brief Get sfTakerPaysIssuer (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTakerPaysIssuer() const + { + if (hasTakerPaysIssuer()) + return this->sle_->at(sfTakerPaysIssuer); + return std::nullopt; + } + + /** + * @brief Check if sfTakerPaysIssuer is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTakerPaysIssuer() const + { + return this->sle_->isFieldPresent(sfTakerPaysIssuer); + } + + /** + * @brief Get sfTakerGetsCurrency (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTakerGetsCurrency() const + { + if (hasTakerGetsCurrency()) + return this->sle_->at(sfTakerGetsCurrency); + return std::nullopt; + } + + /** + * @brief Check if sfTakerGetsCurrency is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTakerGetsCurrency() const + { + return this->sle_->isFieldPresent(sfTakerGetsCurrency); + } + + /** + * @brief Get sfTakerGetsIssuer (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTakerGetsIssuer() const + { + if (hasTakerGetsIssuer()) + return this->sle_->at(sfTakerGetsIssuer); + return std::nullopt; + } + + /** + * @brief Check if sfTakerGetsIssuer is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTakerGetsIssuer() const + { + return this->sle_->isFieldPresent(sfTakerGetsIssuer); + } + + /** + * @brief Get sfExchangeRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExchangeRate() const + { + if (hasExchangeRate()) + return this->sle_->at(sfExchangeRate); + return std::nullopt; + } + + /** + * @brief Check if sfExchangeRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExchangeRate() const + { + return this->sle_->isFieldPresent(sfExchangeRate); + } + + /** + * @brief Get sfIndexes (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VECTOR256::type::value_type + getIndexes() const + { + return this->sle_->at(sfIndexes); + } + + /** + * @brief Get sfRootIndex (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getRootIndex() const + { + return this->sle_->at(sfRootIndex); + } + + /** + * @brief Get sfIndexNext (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getIndexNext() const + { + if (hasIndexNext()) + return this->sle_->at(sfIndexNext); + return std::nullopt; + } + + /** + * @brief Check if sfIndexNext is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasIndexNext() const + { + return this->sle_->isFieldPresent(sfIndexNext); + } + + /** + * @brief Get sfIndexPrevious (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getIndexPrevious() const + { + if (hasIndexPrevious()) + return this->sle_->at(sfIndexPrevious); + return std::nullopt; + } + + /** + * @brief Check if sfIndexPrevious is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasIndexPrevious() const + { + return this->sle_->isFieldPresent(sfIndexPrevious); + } + + /** + * @brief Get sfNFTokenID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNFTokenID() const + { + if (hasNFTokenID()) + return this->sle_->at(sfNFTokenID); + return std::nullopt; + } + + /** + * @brief Check if sfNFTokenID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNFTokenID() const + { + return this->sle_->isFieldPresent(sfNFTokenID); + } + + /** + * @brief Get sfPreviousTxnID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnID() const + { + if (hasPreviousTxnID()) + return this->sle_->at(sfPreviousTxnID); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnID() const + { + return this->sle_->isFieldPresent(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnLgrSeq() const + { + if (hasPreviousTxnLgrSeq()) + return this->sle_->at(sfPreviousTxnLgrSeq); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnLgrSeq is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnLgrSeq() const + { + return this->sle_->isFieldPresent(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + return this->sle_->at(sfDomainID); + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->sle_->isFieldPresent(sfDomainID); + } +}; + +/** + * @brief Builder for DirectoryNode ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class DirectoryNodeBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new DirectoryNodeBuilder with required fields. + * @param indexes The sfIndexes field value. + * @param rootIndex The sfRootIndex field value. + */ + DirectoryNodeBuilder(std::decay_t const& indexes,std::decay_t const& rootIndex) + : LedgerEntryBuilderBase(ltDIR_NODE) + { + setIndexes(indexes); + setRootIndex(rootIndex); + } + + /** + * @brief Construct a DirectoryNodeBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + DirectoryNodeBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltDIR_NODE) + { + throw std::runtime_error("Invalid ledger entry type for DirectoryNode"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfOwner (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfTakerPaysCurrency (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setTakerPaysCurrency(std::decay_t const& value) + { + object_[sfTakerPaysCurrency] = value; + return *this; + } + + /** + * @brief Set sfTakerPaysIssuer (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setTakerPaysIssuer(std::decay_t const& value) + { + object_[sfTakerPaysIssuer] = value; + return *this; + } + + /** + * @brief Set sfTakerGetsCurrency (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setTakerGetsCurrency(std::decay_t const& value) + { + object_[sfTakerGetsCurrency] = value; + return *this; + } + + /** + * @brief Set sfTakerGetsIssuer (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setTakerGetsIssuer(std::decay_t const& value) + { + object_[sfTakerGetsIssuer] = value; + return *this; + } + + /** + * @brief Set sfExchangeRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setExchangeRate(std::decay_t const& value) + { + object_[sfExchangeRate] = value; + return *this; + } + + /** + * @brief Set sfIndexes (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setIndexes(std::decay_t const& value) + { + object_[sfIndexes] = value; + return *this; + } + + /** + * @brief Set sfRootIndex (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setRootIndex(std::decay_t const& value) + { + object_[sfRootIndex] = value; + return *this; + } + + /** + * @brief Set sfIndexNext (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setIndexNext(std::decay_t const& value) + { + object_[sfIndexNext] = value; + return *this; + } + + /** + * @brief Set sfIndexPrevious (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setIndexPrevious(std::decay_t const& value) + { + object_[sfIndexPrevious] = value; + return *this; + } + + /** + * @brief Set sfNFTokenID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setNFTokenID(std::decay_t const& value) + { + object_[sfNFTokenID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Build and return the completed DirectoryNode wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + DirectoryNode + build(uint256 const& index) + { + return DirectoryNode{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Escrow.h b/include/xrpl/protocol_autogen/ledger_entries/Escrow.h new file mode 100644 index 0000000000..fd15b2a713 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Escrow.h @@ -0,0 +1,554 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class EscrowBuilder; + +/** + * @brief Ledger Entry: Escrow + * + * Type: ltESCROW (0x0075) + * RPC Name: escrow + * + * Immutable wrapper around SLE providing type-safe field access. + * Use EscrowBuilder to construct new ledger entries. + */ +class Escrow : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltESCROW; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Escrow"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfSequence (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSequence() const + { + if (hasSequence()) + return this->sle_->at(sfSequence); + return std::nullopt; + } + + /** + * @brief Check if sfSequence is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSequence() const + { + return this->sle_->isFieldPresent(sfSequence); + } + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->sle_->at(sfDestination); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->sle_->at(sfAmount); + } + + /** + * @brief Get sfCondition (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCondition() const + { + if (hasCondition()) + return this->sle_->at(sfCondition); + return std::nullopt; + } + + /** + * @brief Check if sfCondition is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCondition() const + { + return this->sle_->isFieldPresent(sfCondition); + } + + /** + * @brief Get sfCancelAfter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCancelAfter() const + { + if (hasCancelAfter()) + return this->sle_->at(sfCancelAfter); + return std::nullopt; + } + + /** + * @brief Check if sfCancelAfter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCancelAfter() const + { + return this->sle_->isFieldPresent(sfCancelAfter); + } + + /** + * @brief Get sfFinishAfter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getFinishAfter() const + { + if (hasFinishAfter()) + return this->sle_->at(sfFinishAfter); + return std::nullopt; + } + + /** + * @brief Check if sfFinishAfter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasFinishAfter() const + { + return this->sle_->isFieldPresent(sfFinishAfter); + } + + /** + * @brief Get sfSourceTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSourceTag() const + { + if (hasSourceTag()) + return this->sle_->at(sfSourceTag); + return std::nullopt; + } + + /** + * @brief Check if sfSourceTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSourceTag() const + { + return this->sle_->isFieldPresent(sfSourceTag); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationTag() const + { + if (hasDestinationTag()) + return this->sle_->at(sfDestinationTag); + return std::nullopt; + } + + /** + * @brief Check if sfDestinationTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestinationTag() const + { + return this->sle_->isFieldPresent(sfDestinationTag); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfDestinationNode (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationNode() const + { + if (hasDestinationNode()) + return this->sle_->at(sfDestinationNode); + return std::nullopt; + } + + /** + * @brief Check if sfDestinationNode is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestinationNode() const + { + return this->sle_->isFieldPresent(sfDestinationNode); + } + + /** + * @brief Get sfTransferRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTransferRate() const + { + if (hasTransferRate()) + return this->sle_->at(sfTransferRate); + return std::nullopt; + } + + /** + * @brief Check if sfTransferRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTransferRate() const + { + return this->sle_->isFieldPresent(sfTransferRate); + } + + /** + * @brief Get sfIssuerNode (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getIssuerNode() const + { + if (hasIssuerNode()) + return this->sle_->at(sfIssuerNode); + return std::nullopt; + } + + /** + * @brief Check if sfIssuerNode is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasIssuerNode() const + { + return this->sle_->isFieldPresent(sfIssuerNode); + } +}; + +/** + * @brief Builder for Escrow ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class EscrowBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new EscrowBuilder with required fields. + * @param account The sfAccount field value. + * @param destination The sfDestination field value. + * @param amount The sfAmount field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + EscrowBuilder(std::decay_t const& account,std::decay_t const& destination,std::decay_t const& amount,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltESCROW) + { + setAccount(account); + setDestination(destination); + setAmount(amount); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a EscrowBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + EscrowBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltESCROW) + { + throw std::runtime_error("Invalid ledger entry type for Escrow"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfCondition (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setCondition(std::decay_t const& value) + { + object_[sfCondition] = value; + return *this; + } + + /** + * @brief Set sfCancelAfter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setCancelAfter(std::decay_t const& value) + { + object_[sfCancelAfter] = value; + return *this; + } + + /** + * @brief Set sfFinishAfter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setFinishAfter(std::decay_t const& value) + { + object_[sfFinishAfter] = value; + return *this; + } + + /** + * @brief Set sfSourceTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setSourceTag(std::decay_t const& value) + { + object_[sfSourceTag] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfDestinationNode (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setDestinationNode(std::decay_t const& value) + { + object_[sfDestinationNode] = value; + return *this; + } + + /** + * @brief Set sfTransferRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setTransferRate(std::decay_t const& value) + { + object_[sfTransferRate] = value; + return *this; + } + + /** + * @brief Set sfIssuerNode (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setIssuerNode(std::decay_t const& value) + { + object_[sfIssuerNode] = value; + return *this; + } + + /** + * @brief Build and return the completed Escrow wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Escrow + build(uint256 const& index) + { + return Escrow{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h b/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h new file mode 100644 index 0000000000..e3c9ca0091 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h @@ -0,0 +1,410 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class FeeSettingsBuilder; + +/** + * @brief Ledger Entry: FeeSettings + * + * Type: ltFEE_SETTINGS (0x0073) + * RPC Name: fee + * + * Immutable wrapper around SLE providing type-safe field access. + * Use FeeSettingsBuilder to construct new ledger entries. + */ +class FeeSettings : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltFEE_SETTINGS; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for FeeSettings"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfBaseFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBaseFee() const + { + if (hasBaseFee()) + return this->sle_->at(sfBaseFee); + return std::nullopt; + } + + /** + * @brief Check if sfBaseFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBaseFee() const + { + return this->sle_->isFieldPresent(sfBaseFee); + } + + /** + * @brief Get sfReferenceFeeUnits (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReferenceFeeUnits() const + { + if (hasReferenceFeeUnits()) + return this->sle_->at(sfReferenceFeeUnits); + return std::nullopt; + } + + /** + * @brief Check if sfReferenceFeeUnits is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReferenceFeeUnits() const + { + return this->sle_->isFieldPresent(sfReferenceFeeUnits); + } + + /** + * @brief Get sfReserveBase (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveBase() const + { + if (hasReserveBase()) + return this->sle_->at(sfReserveBase); + return std::nullopt; + } + + /** + * @brief Check if sfReserveBase is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveBase() const + { + return this->sle_->isFieldPresent(sfReserveBase); + } + + /** + * @brief Get sfReserveIncrement (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveIncrement() const + { + if (hasReserveIncrement()) + return this->sle_->at(sfReserveIncrement); + return std::nullopt; + } + + /** + * @brief Check if sfReserveIncrement is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveIncrement() const + { + return this->sle_->isFieldPresent(sfReserveIncrement); + } + + /** + * @brief Get sfBaseFeeDrops (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBaseFeeDrops() const + { + if (hasBaseFeeDrops()) + return this->sle_->at(sfBaseFeeDrops); + return std::nullopt; + } + + /** + * @brief Check if sfBaseFeeDrops is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBaseFeeDrops() const + { + return this->sle_->isFieldPresent(sfBaseFeeDrops); + } + + /** + * @brief Get sfReserveBaseDrops (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveBaseDrops() const + { + if (hasReserveBaseDrops()) + return this->sle_->at(sfReserveBaseDrops); + return std::nullopt; + } + + /** + * @brief Check if sfReserveBaseDrops is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveBaseDrops() const + { + return this->sle_->isFieldPresent(sfReserveBaseDrops); + } + + /** + * @brief Get sfReserveIncrementDrops (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveIncrementDrops() const + { + if (hasReserveIncrementDrops()) + return this->sle_->at(sfReserveIncrementDrops); + return std::nullopt; + } + + /** + * @brief Check if sfReserveIncrementDrops is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveIncrementDrops() const + { + return this->sle_->isFieldPresent(sfReserveIncrementDrops); + } + + /** + * @brief Get sfPreviousTxnID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnID() const + { + if (hasPreviousTxnID()) + return this->sle_->at(sfPreviousTxnID); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnID() const + { + return this->sle_->isFieldPresent(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnLgrSeq() const + { + if (hasPreviousTxnLgrSeq()) + return this->sle_->at(sfPreviousTxnLgrSeq); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnLgrSeq is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnLgrSeq() const + { + return this->sle_->isFieldPresent(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for FeeSettings ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class FeeSettingsBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new FeeSettingsBuilder with required fields. + */ + FeeSettingsBuilder() + : LedgerEntryBuilderBase(ltFEE_SETTINGS) + { + } + + /** + * @brief Construct a FeeSettingsBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + FeeSettingsBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltFEE_SETTINGS) + { + throw std::runtime_error("Invalid ledger entry type for FeeSettings"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfBaseFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setBaseFee(std::decay_t const& value) + { + object_[sfBaseFee] = value; + return *this; + } + + /** + * @brief Set sfReferenceFeeUnits (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setReferenceFeeUnits(std::decay_t const& value) + { + object_[sfReferenceFeeUnits] = value; + return *this; + } + + /** + * @brief Set sfReserveBase (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setReserveBase(std::decay_t const& value) + { + object_[sfReserveBase] = value; + return *this; + } + + /** + * @brief Set sfReserveIncrement (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setReserveIncrement(std::decay_t const& value) + { + object_[sfReserveIncrement] = value; + return *this; + } + + /** + * @brief Set sfBaseFeeDrops (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setBaseFeeDrops(std::decay_t const& value) + { + object_[sfBaseFeeDrops] = value; + return *this; + } + + /** + * @brief Set sfReserveBaseDrops (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setReserveBaseDrops(std::decay_t const& value) + { + object_[sfReserveBaseDrops] = value; + return *this; + } + + /** + * @brief Set sfReserveIncrementDrops (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setReserveIncrementDrops(std::decay_t const& value) + { + object_[sfReserveIncrementDrops] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed FeeSettings wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + FeeSettings + build(uint256 const& index) + { + return FeeSettings{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h b/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h new file mode 100644 index 0000000000..c95ca303ba --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h @@ -0,0 +1,189 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class LedgerHashesBuilder; + +/** + * @brief Ledger Entry: LedgerHashes + * + * Type: ltLEDGER_HASHES (0x0068) + * RPC Name: hashes + * + * Immutable wrapper around SLE providing type-safe field access. + * Use LedgerHashesBuilder to construct new ledger entries. + */ +class LedgerHashes : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltLEDGER_HASHES; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for LedgerHashes"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfFirstLedgerSequence (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getFirstLedgerSequence() const + { + if (hasFirstLedgerSequence()) + return this->sle_->at(sfFirstLedgerSequence); + return std::nullopt; + } + + /** + * @brief Check if sfFirstLedgerSequence is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasFirstLedgerSequence() const + { + return this->sle_->isFieldPresent(sfFirstLedgerSequence); + } + + /** + * @brief Get sfLastLedgerSequence (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLastLedgerSequence() const + { + if (hasLastLedgerSequence()) + return this->sle_->at(sfLastLedgerSequence); + return std::nullopt; + } + + /** + * @brief Check if sfLastLedgerSequence is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLastLedgerSequence() const + { + return this->sle_->isFieldPresent(sfLastLedgerSequence); + } + + /** + * @brief Get sfHashes (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VECTOR256::type::value_type + getHashes() const + { + return this->sle_->at(sfHashes); + } +}; + +/** + * @brief Builder for LedgerHashes ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class LedgerHashesBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new LedgerHashesBuilder with required fields. + * @param hashes The sfHashes field value. + */ + LedgerHashesBuilder(std::decay_t const& hashes) + : LedgerEntryBuilderBase(ltLEDGER_HASHES) + { + setHashes(hashes); + } + + /** + * @brief Construct a LedgerHashesBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + LedgerHashesBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltLEDGER_HASHES) + { + throw std::runtime_error("Invalid ledger entry type for LedgerHashes"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfFirstLedgerSequence (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LedgerHashesBuilder& + setFirstLedgerSequence(std::decay_t const& value) + { + object_[sfFirstLedgerSequence] = value; + return *this; + } + + /** + * @brief Set sfLastLedgerSequence (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LedgerHashesBuilder& + setLastLedgerSequence(std::decay_t const& value) + { + object_[sfLastLedgerSequence] = value; + return *this; + } + + /** + * @brief Set sfHashes (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LedgerHashesBuilder& + setHashes(std::decay_t const& value) + { + object_[sfHashes] = value; + return *this; + } + + /** + * @brief Build and return the completed LedgerHashes wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + LedgerHashes + build(uint256 const& index) + { + return LedgerHashes{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Loan.h b/include/xrpl/protocol_autogen/ledger_entries/Loan.h new file mode 100644 index 0000000000..73b37ea892 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Loan.h @@ -0,0 +1,930 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class LoanBuilder; + +/** + * @brief Ledger Entry: Loan + * + * Type: ltLOAN (0x0089) + * RPC Name: loan + * + * Immutable wrapper around SLE providing type-safe field access. + * Use LoanBuilder to construct new ledger entries. + */ +class Loan : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltLOAN; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Loan"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfLoanBrokerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getLoanBrokerNode() const + { + return this->sle_->at(sfLoanBrokerNode); + } + + /** + * @brief Get sfLoanBrokerID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanBrokerID() const + { + return this->sle_->at(sfLoanBrokerID); + } + + /** + * @brief Get sfLoanSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getLoanSequence() const + { + return this->sle_->at(sfLoanSequence); + } + + /** + * @brief Get sfBorrower (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getBorrower() const + { + return this->sle_->at(sfBorrower); + } + + /** + * @brief Get sfLoanOriginationFee (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanOriginationFee() const + { + if (hasLoanOriginationFee()) + return this->sle_->at(sfLoanOriginationFee); + return std::nullopt; + } + + /** + * @brief Check if sfLoanOriginationFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanOriginationFee() const + { + return this->sle_->isFieldPresent(sfLoanOriginationFee); + } + + /** + * @brief Get sfLoanServiceFee (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanServiceFee() const + { + if (hasLoanServiceFee()) + return this->sle_->at(sfLoanServiceFee); + return std::nullopt; + } + + /** + * @brief Check if sfLoanServiceFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanServiceFee() const + { + return this->sle_->isFieldPresent(sfLoanServiceFee); + } + + /** + * @brief Get sfLatePaymentFee (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLatePaymentFee() const + { + if (hasLatePaymentFee()) + return this->sle_->at(sfLatePaymentFee); + return std::nullopt; + } + + /** + * @brief Check if sfLatePaymentFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLatePaymentFee() const + { + return this->sle_->isFieldPresent(sfLatePaymentFee); + } + + /** + * @brief Get sfClosePaymentFee (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getClosePaymentFee() const + { + if (hasClosePaymentFee()) + return this->sle_->at(sfClosePaymentFee); + return std::nullopt; + } + + /** + * @brief Check if sfClosePaymentFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasClosePaymentFee() const + { + return this->sle_->isFieldPresent(sfClosePaymentFee); + } + + /** + * @brief Get sfOverpaymentFee (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOverpaymentFee() const + { + if (hasOverpaymentFee()) + return this->sle_->at(sfOverpaymentFee); + return std::nullopt; + } + + /** + * @brief Check if sfOverpaymentFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOverpaymentFee() const + { + return this->sle_->isFieldPresent(sfOverpaymentFee); + } + + /** + * @brief Get sfInterestRate (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getInterestRate() const + { + if (hasInterestRate()) + return this->sle_->at(sfInterestRate); + return std::nullopt; + } + + /** + * @brief Check if sfInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasInterestRate() const + { + return this->sle_->isFieldPresent(sfInterestRate); + } + + /** + * @brief Get sfLateInterestRate (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLateInterestRate() const + { + if (hasLateInterestRate()) + return this->sle_->at(sfLateInterestRate); + return std::nullopt; + } + + /** + * @brief Check if sfLateInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLateInterestRate() const + { + return this->sle_->isFieldPresent(sfLateInterestRate); + } + + /** + * @brief Get sfCloseInterestRate (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCloseInterestRate() const + { + if (hasCloseInterestRate()) + return this->sle_->at(sfCloseInterestRate); + return std::nullopt; + } + + /** + * @brief Check if sfCloseInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCloseInterestRate() const + { + return this->sle_->isFieldPresent(sfCloseInterestRate); + } + + /** + * @brief Get sfOverpaymentInterestRate (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOverpaymentInterestRate() const + { + if (hasOverpaymentInterestRate()) + return this->sle_->at(sfOverpaymentInterestRate); + return std::nullopt; + } + + /** + * @brief Check if sfOverpaymentInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOverpaymentInterestRate() const + { + return this->sle_->isFieldPresent(sfOverpaymentInterestRate); + } + + /** + * @brief Get sfStartDate (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getStartDate() const + { + return this->sle_->at(sfStartDate); + } + + /** + * @brief Get sfPaymentInterval (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPaymentInterval() const + { + return this->sle_->at(sfPaymentInterval); + } + + /** + * @brief Get sfGracePeriod (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getGracePeriod() const + { + if (hasGracePeriod()) + return this->sle_->at(sfGracePeriod); + return std::nullopt; + } + + /** + * @brief Check if sfGracePeriod is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasGracePeriod() const + { + return this->sle_->isFieldPresent(sfGracePeriod); + } + + /** + * @brief Get sfPreviousPaymentDueDate (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousPaymentDueDate() const + { + if (hasPreviousPaymentDueDate()) + return this->sle_->at(sfPreviousPaymentDueDate); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousPaymentDueDate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousPaymentDueDate() const + { + return this->sle_->isFieldPresent(sfPreviousPaymentDueDate); + } + + /** + * @brief Get sfNextPaymentDueDate (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNextPaymentDueDate() const + { + if (hasNextPaymentDueDate()) + return this->sle_->at(sfNextPaymentDueDate); + return std::nullopt; + } + + /** + * @brief Check if sfNextPaymentDueDate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNextPaymentDueDate() const + { + return this->sle_->isFieldPresent(sfNextPaymentDueDate); + } + + /** + * @brief Get sfPaymentRemaining (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPaymentRemaining() const + { + if (hasPaymentRemaining()) + return this->sle_->at(sfPaymentRemaining); + return std::nullopt; + } + + /** + * @brief Check if sfPaymentRemaining is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPaymentRemaining() const + { + return this->sle_->isFieldPresent(sfPaymentRemaining); + } + + /** + * @brief Get sfPeriodicPayment (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_NUMBER::type::value_type + getPeriodicPayment() const + { + return this->sle_->at(sfPeriodicPayment); + } + + /** + * @brief Get sfPrincipalOutstanding (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPrincipalOutstanding() const + { + if (hasPrincipalOutstanding()) + return this->sle_->at(sfPrincipalOutstanding); + return std::nullopt; + } + + /** + * @brief Check if sfPrincipalOutstanding is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPrincipalOutstanding() const + { + return this->sle_->isFieldPresent(sfPrincipalOutstanding); + } + + /** + * @brief Get sfTotalValueOutstanding (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTotalValueOutstanding() const + { + if (hasTotalValueOutstanding()) + return this->sle_->at(sfTotalValueOutstanding); + return std::nullopt; + } + + /** + * @brief Check if sfTotalValueOutstanding is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTotalValueOutstanding() const + { + return this->sle_->isFieldPresent(sfTotalValueOutstanding); + } + + /** + * @brief Get sfManagementFeeOutstanding (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getManagementFeeOutstanding() const + { + if (hasManagementFeeOutstanding()) + return this->sle_->at(sfManagementFeeOutstanding); + return std::nullopt; + } + + /** + * @brief Check if sfManagementFeeOutstanding is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasManagementFeeOutstanding() const + { + return this->sle_->isFieldPresent(sfManagementFeeOutstanding); + } + + /** + * @brief Get sfLoanScale (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanScale() const + { + if (hasLoanScale()) + return this->sle_->at(sfLoanScale); + return std::nullopt; + } + + /** + * @brief Check if sfLoanScale is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanScale() const + { + return this->sle_->isFieldPresent(sfLoanScale); + } +}; + +/** + * @brief Builder for Loan ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class LoanBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new LoanBuilder with required fields. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + * @param ownerNode The sfOwnerNode field value. + * @param loanBrokerNode The sfLoanBrokerNode field value. + * @param loanBrokerID The sfLoanBrokerID field value. + * @param loanSequence The sfLoanSequence field value. + * @param borrower The sfBorrower field value. + * @param startDate The sfStartDate field value. + * @param paymentInterval The sfPaymentInterval field value. + * @param periodicPayment The sfPeriodicPayment field value. + */ + LoanBuilder(std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq,std::decay_t const& ownerNode,std::decay_t const& loanBrokerNode,std::decay_t const& loanBrokerID,std::decay_t const& loanSequence,std::decay_t const& borrower,std::decay_t const& startDate,std::decay_t const& paymentInterval,std::decay_t const& periodicPayment) + : LedgerEntryBuilderBase(ltLOAN) + { + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + setOwnerNode(ownerNode); + setLoanBrokerNode(loanBrokerNode); + setLoanBrokerID(loanBrokerID); + setLoanSequence(loanSequence); + setBorrower(borrower); + setStartDate(startDate); + setPaymentInterval(paymentInterval); + setPeriodicPayment(periodicPayment); + } + + /** + * @brief Construct a LoanBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + LoanBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltLOAN) + { + throw std::runtime_error("Invalid ledger entry type for Loan"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfLoanBrokerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLoanBrokerNode(std::decay_t const& value) + { + object_[sfLoanBrokerNode] = value; + return *this; + } + + /** + * @brief Set sfLoanBrokerID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Set sfLoanSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLoanSequence(std::decay_t const& value) + { + object_[sfLoanSequence] = value; + return *this; + } + + /** + * @brief Set sfBorrower (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setBorrower(std::decay_t const& value) + { + object_[sfBorrower] = value; + return *this; + } + + /** + * @brief Set sfLoanOriginationFee (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLoanOriginationFee(std::decay_t const& value) + { + object_[sfLoanOriginationFee] = value; + return *this; + } + + /** + * @brief Set sfLoanServiceFee (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLoanServiceFee(std::decay_t const& value) + { + object_[sfLoanServiceFee] = value; + return *this; + } + + /** + * @brief Set sfLatePaymentFee (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLatePaymentFee(std::decay_t const& value) + { + object_[sfLatePaymentFee] = value; + return *this; + } + + /** + * @brief Set sfClosePaymentFee (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setClosePaymentFee(std::decay_t const& value) + { + object_[sfClosePaymentFee] = value; + return *this; + } + + /** + * @brief Set sfOverpaymentFee (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setOverpaymentFee(std::decay_t const& value) + { + object_[sfOverpaymentFee] = value; + return *this; + } + + /** + * @brief Set sfInterestRate (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setInterestRate(std::decay_t const& value) + { + object_[sfInterestRate] = value; + return *this; + } + + /** + * @brief Set sfLateInterestRate (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLateInterestRate(std::decay_t const& value) + { + object_[sfLateInterestRate] = value; + return *this; + } + + /** + * @brief Set sfCloseInterestRate (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setCloseInterestRate(std::decay_t const& value) + { + object_[sfCloseInterestRate] = value; + return *this; + } + + /** + * @brief Set sfOverpaymentInterestRate (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setOverpaymentInterestRate(std::decay_t const& value) + { + object_[sfOverpaymentInterestRate] = value; + return *this; + } + + /** + * @brief Set sfStartDate (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setStartDate(std::decay_t const& value) + { + object_[sfStartDate] = value; + return *this; + } + + /** + * @brief Set sfPaymentInterval (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setPaymentInterval(std::decay_t const& value) + { + object_[sfPaymentInterval] = value; + return *this; + } + + /** + * @brief Set sfGracePeriod (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setGracePeriod(std::decay_t const& value) + { + object_[sfGracePeriod] = value; + return *this; + } + + /** + * @brief Set sfPreviousPaymentDueDate (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setPreviousPaymentDueDate(std::decay_t const& value) + { + object_[sfPreviousPaymentDueDate] = value; + return *this; + } + + /** + * @brief Set sfNextPaymentDueDate (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setNextPaymentDueDate(std::decay_t const& value) + { + object_[sfNextPaymentDueDate] = value; + return *this; + } + + /** + * @brief Set sfPaymentRemaining (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setPaymentRemaining(std::decay_t const& value) + { + object_[sfPaymentRemaining] = value; + return *this; + } + + /** + * @brief Set sfPeriodicPayment (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setPeriodicPayment(std::decay_t const& value) + { + object_[sfPeriodicPayment] = value; + return *this; + } + + /** + * @brief Set sfPrincipalOutstanding (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setPrincipalOutstanding(std::decay_t const& value) + { + object_[sfPrincipalOutstanding] = value; + return *this; + } + + /** + * @brief Set sfTotalValueOutstanding (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setTotalValueOutstanding(std::decay_t const& value) + { + object_[sfTotalValueOutstanding] = value; + return *this; + } + + /** + * @brief Set sfManagementFeeOutstanding (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setManagementFeeOutstanding(std::decay_t const& value) + { + object_[sfManagementFeeOutstanding] = value; + return *this; + } + + /** + * @brief Set sfLoanScale (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLoanScale(std::decay_t const& value) + { + object_[sfLoanScale] = value; + return *this; + } + + /** + * @brief Build and return the completed Loan wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Loan + build(uint256 const& index) + { + return Loan{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h b/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h new file mode 100644 index 0000000000..99083d6fce --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h @@ -0,0 +1,591 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class LoanBrokerBuilder; + +/** + * @brief Ledger Entry: LoanBroker + * + * Type: ltLOAN_BROKER (0x0088) + * RPC Name: loan_broker + * + * Immutable wrapper around SLE providing type-safe field access. + * Use LoanBrokerBuilder to construct new ledger entries. + */ +class LoanBroker : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltLOAN_BROKER; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for LoanBroker"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSequence() const + { + return this->sle_->at(sfSequence); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfVaultNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getVaultNode() const + { + return this->sle_->at(sfVaultNode); + } + + /** + * @brief Get sfVaultID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getVaultID() const + { + return this->sle_->at(sfVaultID); + } + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfOwner (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOwner() const + { + return this->sle_->at(sfOwner); + } + + /** + * @brief Get sfLoanSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getLoanSequence() const + { + return this->sle_->at(sfLoanSequence); + } + + /** + * @brief Get sfData (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getData() const + { + if (hasData()) + return this->sle_->at(sfData); + return std::nullopt; + } + + /** + * @brief Check if sfData is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasData() const + { + return this->sle_->isFieldPresent(sfData); + } + + /** + * @brief Get sfManagementFeeRate (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getManagementFeeRate() const + { + if (hasManagementFeeRate()) + return this->sle_->at(sfManagementFeeRate); + return std::nullopt; + } + + /** + * @brief Check if sfManagementFeeRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasManagementFeeRate() const + { + return this->sle_->isFieldPresent(sfManagementFeeRate); + } + + /** + * @brief Get sfOwnerCount (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOwnerCount() const + { + if (hasOwnerCount()) + return this->sle_->at(sfOwnerCount); + return std::nullopt; + } + + /** + * @brief Check if sfOwnerCount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOwnerCount() const + { + return this->sle_->isFieldPresent(sfOwnerCount); + } + + /** + * @brief Get sfDebtTotal (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDebtTotal() const + { + if (hasDebtTotal()) + return this->sle_->at(sfDebtTotal); + return std::nullopt; + } + + /** + * @brief Check if sfDebtTotal is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDebtTotal() const + { + return this->sle_->isFieldPresent(sfDebtTotal); + } + + /** + * @brief Get sfDebtMaximum (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDebtMaximum() const + { + if (hasDebtMaximum()) + return this->sle_->at(sfDebtMaximum); + return std::nullopt; + } + + /** + * @brief Check if sfDebtMaximum is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDebtMaximum() const + { + return this->sle_->isFieldPresent(sfDebtMaximum); + } + + /** + * @brief Get sfCoverAvailable (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCoverAvailable() const + { + if (hasCoverAvailable()) + return this->sle_->at(sfCoverAvailable); + return std::nullopt; + } + + /** + * @brief Check if sfCoverAvailable is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCoverAvailable() const + { + return this->sle_->isFieldPresent(sfCoverAvailable); + } + + /** + * @brief Get sfCoverRateMinimum (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCoverRateMinimum() const + { + if (hasCoverRateMinimum()) + return this->sle_->at(sfCoverRateMinimum); + return std::nullopt; + } + + /** + * @brief Check if sfCoverRateMinimum is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCoverRateMinimum() const + { + return this->sle_->isFieldPresent(sfCoverRateMinimum); + } + + /** + * @brief Get sfCoverRateLiquidation (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCoverRateLiquidation() const + { + if (hasCoverRateLiquidation()) + return this->sle_->at(sfCoverRateLiquidation); + return std::nullopt; + } + + /** + * @brief Check if sfCoverRateLiquidation is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCoverRateLiquidation() const + { + return this->sle_->isFieldPresent(sfCoverRateLiquidation); + } +}; + +/** + * @brief Builder for LoanBroker ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class LoanBrokerBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new LoanBrokerBuilder with required fields. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + * @param sequence The sfSequence field value. + * @param ownerNode The sfOwnerNode field value. + * @param vaultNode The sfVaultNode field value. + * @param vaultID The sfVaultID field value. + * @param account The sfAccount field value. + * @param owner The sfOwner field value. + * @param loanSequence The sfLoanSequence field value. + */ + LoanBrokerBuilder(std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq,std::decay_t const& sequence,std::decay_t const& ownerNode,std::decay_t const& vaultNode,std::decay_t const& vaultID,std::decay_t const& account,std::decay_t const& owner,std::decay_t const& loanSequence) + : LedgerEntryBuilderBase(ltLOAN_BROKER) + { + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + setSequence(sequence); + setOwnerNode(ownerNode); + setVaultNode(vaultNode); + setVaultID(vaultID); + setAccount(account); + setOwner(owner); + setLoanSequence(loanSequence); + } + + /** + * @brief Construct a LoanBrokerBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + LoanBrokerBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltLOAN_BROKER) + { + throw std::runtime_error("Invalid ledger entry type for LoanBroker"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfVaultNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setVaultNode(std::decay_t const& value) + { + object_[sfVaultNode] = value; + return *this; + } + + /** + * @brief Set sfVaultID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfOwner (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfLoanSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setLoanSequence(std::decay_t const& value) + { + object_[sfLoanSequence] = value; + return *this; + } + + /** + * @brief Set sfData (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Set sfManagementFeeRate (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setManagementFeeRate(std::decay_t const& value) + { + object_[sfManagementFeeRate] = value; + return *this; + } + + /** + * @brief Set sfOwnerCount (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setOwnerCount(std::decay_t const& value) + { + object_[sfOwnerCount] = value; + return *this; + } + + /** + * @brief Set sfDebtTotal (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setDebtTotal(std::decay_t const& value) + { + object_[sfDebtTotal] = value; + return *this; + } + + /** + * @brief Set sfDebtMaximum (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setDebtMaximum(std::decay_t const& value) + { + object_[sfDebtMaximum] = value; + return *this; + } + + /** + * @brief Set sfCoverAvailable (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setCoverAvailable(std::decay_t const& value) + { + object_[sfCoverAvailable] = value; + return *this; + } + + /** + * @brief Set sfCoverRateMinimum (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setCoverRateMinimum(std::decay_t const& value) + { + object_[sfCoverRateMinimum] = value; + return *this; + } + + /** + * @brief Set sfCoverRateLiquidation (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setCoverRateLiquidation(std::decay_t const& value) + { + object_[sfCoverRateLiquidation] = value; + return *this; + } + + /** + * @brief Build and return the completed LoanBroker wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + LoanBroker + build(uint256 const& index) + { + return LoanBroker{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/MPToken.h b/include/xrpl/protocol_autogen/ledger_entries/MPToken.h new file mode 100644 index 0000000000..d11a8effd3 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/MPToken.h @@ -0,0 +1,285 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class MPTokenBuilder; + +/** + * @brief Ledger Entry: MPToken + * + * Type: ltMPTOKEN (0x007f) + * RPC Name: mptoken + * + * Immutable wrapper around SLE providing type-safe field access. + * Use MPTokenBuilder to construct new ledger entries. + */ +class MPToken : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltMPTOKEN; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for MPToken"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfMPTokenIssuanceID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT192::type::value_type + getMPTokenIssuanceID() const + { + return this->sle_->at(sfMPTokenIssuanceID); + } + + /** + * @brief Get sfMPTAmount (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMPTAmount() const + { + if (hasMPTAmount()) + return this->sle_->at(sfMPTAmount); + return std::nullopt; + } + + /** + * @brief Check if sfMPTAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMPTAmount() const + { + return this->sle_->isFieldPresent(sfMPTAmount); + } + + /** + * @brief Get sfLockedAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLockedAmount() const + { + if (hasLockedAmount()) + return this->sle_->at(sfLockedAmount); + return std::nullopt; + } + + /** + * @brief Check if sfLockedAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLockedAmount() const + { + return this->sle_->isFieldPresent(sfLockedAmount); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for MPToken ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class MPTokenBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new MPTokenBuilder with required fields. + * @param account The sfAccount field value. + * @param mPTokenIssuanceID The sfMPTokenIssuanceID field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + MPTokenBuilder(std::decay_t const& account,std::decay_t const& mPTokenIssuanceID,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltMPTOKEN) + { + setAccount(account); + setMPTokenIssuanceID(mPTokenIssuanceID); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a MPTokenBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + MPTokenBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltMPTOKEN) + { + throw std::runtime_error("Invalid ledger entry type for MPToken"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfMPTokenIssuanceID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenBuilder& + setMPTokenIssuanceID(std::decay_t const& value) + { + object_[sfMPTokenIssuanceID] = value; + return *this; + } + + /** + * @brief Set sfMPTAmount (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + MPTokenBuilder& + setMPTAmount(std::decay_t const& value) + { + object_[sfMPTAmount] = value; + return *this; + } + + /** + * @brief Set sfLockedAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenBuilder& + setLockedAmount(std::decay_t const& value) + { + object_[sfLockedAmount] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed MPToken wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + MPToken + build(uint256 const& index) + { + return MPToken{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h b/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h new file mode 100644 index 0000000000..23b8a05015 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h @@ -0,0 +1,484 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class MPTokenIssuanceBuilder; + +/** + * @brief Ledger Entry: MPTokenIssuance + * + * Type: ltMPTOKEN_ISSUANCE (0x007e) + * RPC Name: mpt_issuance + * + * Immutable wrapper around SLE providing type-safe field access. + * Use MPTokenIssuanceBuilder to construct new ledger entries. + */ +class MPTokenIssuance : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltMPTOKEN_ISSUANCE; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for MPTokenIssuance"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfIssuer (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getIssuer() const + { + return this->sle_->at(sfIssuer); + } + + /** + * @brief Get sfSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSequence() const + { + return this->sle_->at(sfSequence); + } + + /** + * @brief Get sfTransferFee (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTransferFee() const + { + if (hasTransferFee()) + return this->sle_->at(sfTransferFee); + return std::nullopt; + } + + /** + * @brief Check if sfTransferFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTransferFee() const + { + return this->sle_->isFieldPresent(sfTransferFee); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfAssetScale (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetScale() const + { + if (hasAssetScale()) + return this->sle_->at(sfAssetScale); + return std::nullopt; + } + + /** + * @brief Check if sfAssetScale is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetScale() const + { + return this->sle_->isFieldPresent(sfAssetScale); + } + + /** + * @brief Get sfMaximumAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMaximumAmount() const + { + if (hasMaximumAmount()) + return this->sle_->at(sfMaximumAmount); + return std::nullopt; + } + + /** + * @brief Check if sfMaximumAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMaximumAmount() const + { + return this->sle_->isFieldPresent(sfMaximumAmount); + } + + /** + * @brief Get sfOutstandingAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOutstandingAmount() const + { + return this->sle_->at(sfOutstandingAmount); + } + + /** + * @brief Get sfLockedAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLockedAmount() const + { + if (hasLockedAmount()) + return this->sle_->at(sfLockedAmount); + return std::nullopt; + } + + /** + * @brief Check if sfLockedAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLockedAmount() const + { + return this->sle_->isFieldPresent(sfLockedAmount); + } + + /** + * @brief Get sfMPTokenMetadata (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMPTokenMetadata() const + { + if (hasMPTokenMetadata()) + return this->sle_->at(sfMPTokenMetadata); + return std::nullopt; + } + + /** + * @brief Check if sfMPTokenMetadata is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMPTokenMetadata() const + { + return this->sle_->isFieldPresent(sfMPTokenMetadata); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + return this->sle_->at(sfDomainID); + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->sle_->isFieldPresent(sfDomainID); + } + + /** + * @brief Get sfMutableFlags (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMutableFlags() const + { + if (hasMutableFlags()) + return this->sle_->at(sfMutableFlags); + return std::nullopt; + } + + /** + * @brief Check if sfMutableFlags is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMutableFlags() const + { + return this->sle_->isFieldPresent(sfMutableFlags); + } +}; + +/** + * @brief Builder for MPTokenIssuance ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class MPTokenIssuanceBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new MPTokenIssuanceBuilder with required fields. + * @param issuer The sfIssuer field value. + * @param sequence The sfSequence field value. + * @param ownerNode The sfOwnerNode field value. + * @param outstandingAmount The sfOutstandingAmount field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + MPTokenIssuanceBuilder(std::decay_t const& issuer,std::decay_t const& sequence,std::decay_t const& ownerNode,std::decay_t const& outstandingAmount,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltMPTOKEN_ISSUANCE) + { + setIssuer(issuer); + setSequence(sequence); + setOwnerNode(ownerNode); + setOutstandingAmount(outstandingAmount); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a MPTokenIssuanceBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + MPTokenIssuanceBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltMPTOKEN_ISSUANCE) + { + throw std::runtime_error("Invalid ledger entry type for MPTokenIssuance"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfIssuer (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setIssuer(std::decay_t const& value) + { + object_[sfIssuer] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfTransferFee (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setTransferFee(std::decay_t const& value) + { + object_[sfTransferFee] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfAssetScale (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setAssetScale(std::decay_t const& value) + { + object_[sfAssetScale] = value; + return *this; + } + + /** + * @brief Set sfMaximumAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setMaximumAmount(std::decay_t const& value) + { + object_[sfMaximumAmount] = value; + return *this; + } + + /** + * @brief Set sfOutstandingAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setOutstandingAmount(std::decay_t const& value) + { + object_[sfOutstandingAmount] = value; + return *this; + } + + /** + * @brief Set sfLockedAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setLockedAmount(std::decay_t const& value) + { + object_[sfLockedAmount] = value; + return *this; + } + + /** + * @brief Set sfMPTokenMetadata (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setMPTokenMetadata(std::decay_t const& value) + { + object_[sfMPTokenMetadata] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Set sfMutableFlags (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setMutableFlags(std::decay_t const& value) + { + object_[sfMutableFlags] = value; + return *this; + } + + /** + * @brief Build and return the completed MPTokenIssuance wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + MPTokenIssuance + build(uint256 const& index) + { + return MPTokenIssuance{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h b/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h new file mode 100644 index 0000000000..c500cde14c --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h @@ -0,0 +1,333 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class NFTokenOfferBuilder; + +/** + * @brief Ledger Entry: NFTokenOffer + * + * Type: ltNFTOKEN_OFFER (0x0037) + * RPC Name: nft_offer + * + * Immutable wrapper around SLE providing type-safe field access. + * Use NFTokenOfferBuilder to construct new ledger entries. + */ +class NFTokenOffer : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltNFTOKEN_OFFER; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for NFTokenOffer"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfOwner (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOwner() const + { + return this->sle_->at(sfOwner); + } + + /** + * @brief Get sfNFTokenID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getNFTokenID() const + { + return this->sle_->at(sfNFTokenID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->sle_->at(sfAmount); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfNFTokenOfferNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getNFTokenOfferNode() const + { + return this->sle_->at(sfNFTokenOfferNode); + } + + /** + * @brief Get sfDestination (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestination() const + { + if (hasDestination()) + return this->sle_->at(sfDestination); + return std::nullopt; + } + + /** + * @brief Check if sfDestination is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestination() const + { + return this->sle_->isFieldPresent(sfDestination); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + return this->sle_->at(sfExpiration); + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->sle_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for NFTokenOffer ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class NFTokenOfferBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenOfferBuilder with required fields. + * @param owner The sfOwner field value. + * @param nFTokenID The sfNFTokenID field value. + * @param amount The sfAmount field value. + * @param ownerNode The sfOwnerNode field value. + * @param nFTokenOfferNode The sfNFTokenOfferNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + NFTokenOfferBuilder(std::decay_t const& owner,std::decay_t const& nFTokenID,std::decay_t const& amount,std::decay_t const& ownerNode,std::decay_t const& nFTokenOfferNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltNFTOKEN_OFFER) + { + setOwner(owner); + setNFTokenID(nFTokenID); + setAmount(amount); + setOwnerNode(ownerNode); + setNFTokenOfferNode(nFTokenOfferNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a NFTokenOfferBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + NFTokenOfferBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltNFTOKEN_OFFER) + { + throw std::runtime_error("Invalid ledger entry type for NFTokenOffer"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfOwner (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfNFTokenID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setNFTokenID(std::decay_t const& value) + { + object_[sfNFTokenID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfNFTokenOfferNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setNFTokenOfferNode(std::decay_t const& value) + { + object_[sfNFTokenOfferNode] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed NFTokenOffer wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + NFTokenOffer + build(uint256 const& index) + { + return NFTokenOffer{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h b/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h new file mode 100644 index 0000000000..fa8c415495 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h @@ -0,0 +1,238 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class NFTokenPageBuilder; + +/** + * @brief Ledger Entry: NFTokenPage + * + * Type: ltNFTOKEN_PAGE (0x0050) + * RPC Name: nft_page + * + * Immutable wrapper around SLE providing type-safe field access. + * Use NFTokenPageBuilder to construct new ledger entries. + */ +class NFTokenPage : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltNFTOKEN_PAGE; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for NFTokenPage"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfPreviousPageMin (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousPageMin() const + { + if (hasPreviousPageMin()) + return this->sle_->at(sfPreviousPageMin); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousPageMin is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousPageMin() const + { + return this->sle_->isFieldPresent(sfPreviousPageMin); + } + + /** + * @brief Get sfNextPageMin (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNextPageMin() const + { + if (hasNextPageMin()) + return this->sle_->at(sfNextPageMin); + return std::nullopt; + } + + /** + * @brief Check if sfNextPageMin is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNextPageMin() const + { + return this->sle_->isFieldPresent(sfNextPageMin); + } + + /** + * @brief Get sfNFTokens (soeREQUIRED) + * @note This is an untyped field (unknown). + * @return The field value. + */ + [[nodiscard]] + STArray const& + getNFTokens() const + { + return this->sle_->getFieldArray(sfNFTokens); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for NFTokenPage ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class NFTokenPageBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenPageBuilder with required fields. + * @param nFTokens The sfNFTokens field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + NFTokenPageBuilder(STArray const& nFTokens,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltNFTOKEN_PAGE) + { + setNFTokens(nFTokens); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a NFTokenPageBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + NFTokenPageBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltNFTOKEN_PAGE) + { + throw std::runtime_error("Invalid ledger entry type for NFTokenPage"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfPreviousPageMin (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenPageBuilder& + setPreviousPageMin(std::decay_t const& value) + { + object_[sfPreviousPageMin] = value; + return *this; + } + + /** + * @brief Set sfNextPageMin (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenPageBuilder& + setNextPageMin(std::decay_t const& value) + { + object_[sfNextPageMin] = value; + return *this; + } + + /** + * @brief Set sfNFTokens (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenPageBuilder& + setNFTokens(STArray const& value) + { + object_.setFieldArray(sfNFTokens, value); + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenPageBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenPageBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed NFTokenPage wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + NFTokenPage + build(uint256 const& index) + { + return NFTokenPage{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h b/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h new file mode 100644 index 0000000000..8ac2db5683 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h @@ -0,0 +1,271 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class NegativeUNLBuilder; + +/** + * @brief Ledger Entry: NegativeUNL + * + * Type: ltNEGATIVE_UNL (0x004e) + * RPC Name: nunl + * + * Immutable wrapper around SLE providing type-safe field access. + * Use NegativeUNLBuilder to construct new ledger entries. + */ +class NegativeUNL : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltNEGATIVE_UNL; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for NegativeUNL"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfDisabledValidators (soeOPTIONAL) + * @note This is an untyped field (unknown). + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getDisabledValidators() const + { + if (this->sle_->isFieldPresent(sfDisabledValidators)) + return this->sle_->getFieldArray(sfDisabledValidators); + return std::nullopt; + } + + /** + * @brief Check if sfDisabledValidators is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDisabledValidators() const + { + return this->sle_->isFieldPresent(sfDisabledValidators); + } + + /** + * @brief Get sfValidatorToDisable (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getValidatorToDisable() const + { + if (hasValidatorToDisable()) + return this->sle_->at(sfValidatorToDisable); + return std::nullopt; + } + + /** + * @brief Check if sfValidatorToDisable is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasValidatorToDisable() const + { + return this->sle_->isFieldPresent(sfValidatorToDisable); + } + + /** + * @brief Get sfValidatorToReEnable (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getValidatorToReEnable() const + { + if (hasValidatorToReEnable()) + return this->sle_->at(sfValidatorToReEnable); + return std::nullopt; + } + + /** + * @brief Check if sfValidatorToReEnable is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasValidatorToReEnable() const + { + return this->sle_->isFieldPresent(sfValidatorToReEnable); + } + + /** + * @brief Get sfPreviousTxnID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnID() const + { + if (hasPreviousTxnID()) + return this->sle_->at(sfPreviousTxnID); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnID() const + { + return this->sle_->isFieldPresent(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnLgrSeq() const + { + if (hasPreviousTxnLgrSeq()) + return this->sle_->at(sfPreviousTxnLgrSeq); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnLgrSeq is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnLgrSeq() const + { + return this->sle_->isFieldPresent(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for NegativeUNL ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class NegativeUNLBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new NegativeUNLBuilder with required fields. + */ + NegativeUNLBuilder() + : LedgerEntryBuilderBase(ltNEGATIVE_UNL) + { + } + + /** + * @brief Construct a NegativeUNLBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + NegativeUNLBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltNEGATIVE_UNL) + { + throw std::runtime_error("Invalid ledger entry type for NegativeUNL"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfDisabledValidators (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NegativeUNLBuilder& + setDisabledValidators(STArray const& value) + { + object_.setFieldArray(sfDisabledValidators, value); + return *this; + } + + /** + * @brief Set sfValidatorToDisable (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NegativeUNLBuilder& + setValidatorToDisable(std::decay_t const& value) + { + object_[sfValidatorToDisable] = value; + return *this; + } + + /** + * @brief Set sfValidatorToReEnable (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NegativeUNLBuilder& + setValidatorToReEnable(std::decay_t const& value) + { + object_[sfValidatorToReEnable] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NegativeUNLBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NegativeUNLBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed NegativeUNL wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + NegativeUNL + build(uint256 const& index) + { + return NegativeUNL{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Offer.h b/include/xrpl/protocol_autogen/ledger_entries/Offer.h new file mode 100644 index 0000000000..cccd9183a1 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Offer.h @@ -0,0 +1,417 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class OfferBuilder; + +/** + * @brief Ledger Entry: Offer + * + * Type: ltOFFER (0x006f) + * RPC Name: offer + * + * Immutable wrapper around SLE providing type-safe field access. + * Use OfferBuilder to construct new ledger entries. + */ +class Offer : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltOFFER; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Offer"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSequence() const + { + return this->sle_->at(sfSequence); + } + + /** + * @brief Get sfTakerPays (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getTakerPays() const + { + return this->sle_->at(sfTakerPays); + } + + /** + * @brief Get sfTakerGets (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getTakerGets() const + { + return this->sle_->at(sfTakerGets); + } + + /** + * @brief Get sfBookDirectory (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getBookDirectory() const + { + return this->sle_->at(sfBookDirectory); + } + + /** + * @brief Get sfBookNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getBookNode() const + { + return this->sle_->at(sfBookNode); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + return this->sle_->at(sfExpiration); + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->sle_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + return this->sle_->at(sfDomainID); + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->sle_->isFieldPresent(sfDomainID); + } + + /** + * @brief Get sfAdditionalBooks (soeOPTIONAL) + * @note This is an untyped field (unknown). + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getAdditionalBooks() const + { + if (this->sle_->isFieldPresent(sfAdditionalBooks)) + return this->sle_->getFieldArray(sfAdditionalBooks); + return std::nullopt; + } + + /** + * @brief Check if sfAdditionalBooks is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAdditionalBooks() const + { + return this->sle_->isFieldPresent(sfAdditionalBooks); + } +}; + +/** + * @brief Builder for Offer ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class OfferBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new OfferBuilder with required fields. + * @param account The sfAccount field value. + * @param sequence The sfSequence field value. + * @param takerPays The sfTakerPays field value. + * @param takerGets The sfTakerGets field value. + * @param bookDirectory The sfBookDirectory field value. + * @param bookNode The sfBookNode field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + OfferBuilder(std::decay_t const& account,std::decay_t const& sequence,std::decay_t const& takerPays,std::decay_t const& takerGets,std::decay_t const& bookDirectory,std::decay_t const& bookNode,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltOFFER) + { + setAccount(account); + setSequence(sequence); + setTakerPays(takerPays); + setTakerGets(takerGets); + setBookDirectory(bookDirectory); + setBookNode(bookNode); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a OfferBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + OfferBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltOFFER) + { + throw std::runtime_error("Invalid ledger entry type for Offer"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfTakerPays (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setTakerPays(std::decay_t const& value) + { + object_[sfTakerPays] = value; + return *this; + } + + /** + * @brief Set sfTakerGets (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setTakerGets(std::decay_t const& value) + { + object_[sfTakerGets] = value; + return *this; + } + + /** + * @brief Set sfBookDirectory (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setBookDirectory(std::decay_t const& value) + { + object_[sfBookDirectory] = value; + return *this; + } + + /** + * @brief Set sfBookNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setBookNode(std::decay_t const& value) + { + object_[sfBookNode] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Set sfAdditionalBooks (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setAdditionalBooks(STArray const& value) + { + object_.setFieldArray(sfAdditionalBooks, value); + return *this; + } + + /** + * @brief Build and return the completed Offer wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Offer + build(uint256 const& index) + { + return Offer{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Oracle.h b/include/xrpl/protocol_autogen/ledger_entries/Oracle.h new file mode 100644 index 0000000000..4da93773e6 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Oracle.h @@ -0,0 +1,358 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class OracleBuilder; + +/** + * @brief Ledger Entry: Oracle + * + * Type: ltORACLE (0x0080) + * RPC Name: oracle + * + * Immutable wrapper around SLE providing type-safe field access. + * Use OracleBuilder to construct new ledger entries. + */ +class Oracle : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltORACLE; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Oracle"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfOwner (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOwner() const + { + return this->sle_->at(sfOwner); + } + + /** + * @brief Get sfOracleDocumentID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOracleDocumentID() const + { + if (hasOracleDocumentID()) + return this->sle_->at(sfOracleDocumentID); + return std::nullopt; + } + + /** + * @brief Check if sfOracleDocumentID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOracleDocumentID() const + { + return this->sle_->isFieldPresent(sfOracleDocumentID); + } + + /** + * @brief Get sfProvider (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getProvider() const + { + return this->sle_->at(sfProvider); + } + + /** + * @brief Get sfPriceDataSeries (soeREQUIRED) + * @note This is an untyped field (unknown). + * @return The field value. + */ + [[nodiscard]] + STArray const& + getPriceDataSeries() const + { + return this->sle_->getFieldArray(sfPriceDataSeries); + } + + /** + * @brief Get sfAssetClass (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getAssetClass() const + { + return this->sle_->at(sfAssetClass); + } + + /** + * @brief Get sfLastUpdateTime (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getLastUpdateTime() const + { + return this->sle_->at(sfLastUpdateTime); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + return this->sle_->at(sfURI); + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->sle_->isFieldPresent(sfURI); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for Oracle ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class OracleBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new OracleBuilder with required fields. + * @param owner The sfOwner field value. + * @param provider The sfProvider field value. + * @param priceDataSeries The sfPriceDataSeries field value. + * @param assetClass The sfAssetClass field value. + * @param lastUpdateTime The sfLastUpdateTime field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + OracleBuilder(std::decay_t const& owner,std::decay_t const& provider,STArray const& priceDataSeries,std::decay_t const& assetClass,std::decay_t const& lastUpdateTime,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltORACLE) + { + setOwner(owner); + setProvider(provider); + setPriceDataSeries(priceDataSeries); + setAssetClass(assetClass); + setLastUpdateTime(lastUpdateTime); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a OracleBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + OracleBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltORACLE) + { + throw std::runtime_error("Invalid ledger entry type for Oracle"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfOwner (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfOracleDocumentID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setOracleDocumentID(std::decay_t const& value) + { + object_[sfOracleDocumentID] = value; + return *this; + } + + /** + * @brief Set sfProvider (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setProvider(std::decay_t const& value) + { + object_[sfProvider] = value; + return *this; + } + + /** + * @brief Set sfPriceDataSeries (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setPriceDataSeries(STArray const& value) + { + object_.setFieldArray(sfPriceDataSeries, value); + return *this; + } + + /** + * @brief Set sfAssetClass (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setAssetClass(std::decay_t const& value) + { + object_[sfAssetClass] = value; + return *this; + } + + /** + * @brief Set sfLastUpdateTime (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setLastUpdateTime(std::decay_t const& value) + { + object_[sfLastUpdateTime] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed Oracle wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Oracle + build(uint256 const& index) + { + return Oracle{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h b/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h new file mode 100644 index 0000000000..50245bc0d8 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h @@ -0,0 +1,521 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class PayChannelBuilder; + +/** + * @brief Ledger Entry: PayChannel + * + * Type: ltPAYCHAN (0x0078) + * RPC Name: payment_channel + * + * Immutable wrapper around SLE providing type-safe field access. + * Use PayChannelBuilder to construct new ledger entries. + */ +class PayChannel : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltPAYCHAN; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for PayChannel"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->sle_->at(sfDestination); + } + + /** + * @brief Get sfSequence (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSequence() const + { + if (hasSequence()) + return this->sle_->at(sfSequence); + return std::nullopt; + } + + /** + * @brief Check if sfSequence is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSequence() const + { + return this->sle_->isFieldPresent(sfSequence); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->sle_->at(sfAmount); + } + + /** + * @brief Get sfBalance (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getBalance() const + { + return this->sle_->at(sfBalance); + } + + /** + * @brief Get sfPublicKey (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getPublicKey() const + { + return this->sle_->at(sfPublicKey); + } + + /** + * @brief Get sfSettleDelay (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSettleDelay() const + { + return this->sle_->at(sfSettleDelay); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + return this->sle_->at(sfExpiration); + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->sle_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfCancelAfter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCancelAfter() const + { + if (hasCancelAfter()) + return this->sle_->at(sfCancelAfter); + return std::nullopt; + } + + /** + * @brief Check if sfCancelAfter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCancelAfter() const + { + return this->sle_->isFieldPresent(sfCancelAfter); + } + + /** + * @brief Get sfSourceTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSourceTag() const + { + if (hasSourceTag()) + return this->sle_->at(sfSourceTag); + return std::nullopt; + } + + /** + * @brief Check if sfSourceTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSourceTag() const + { + return this->sle_->isFieldPresent(sfSourceTag); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationTag() const + { + if (hasDestinationTag()) + return this->sle_->at(sfDestinationTag); + return std::nullopt; + } + + /** + * @brief Check if sfDestinationTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestinationTag() const + { + return this->sle_->isFieldPresent(sfDestinationTag); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfDestinationNode (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationNode() const + { + if (hasDestinationNode()) + return this->sle_->at(sfDestinationNode); + return std::nullopt; + } + + /** + * @brief Check if sfDestinationNode is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestinationNode() const + { + return this->sle_->isFieldPresent(sfDestinationNode); + } +}; + +/** + * @brief Builder for PayChannel ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class PayChannelBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new PayChannelBuilder with required fields. + * @param account The sfAccount field value. + * @param destination The sfDestination field value. + * @param amount The sfAmount field value. + * @param balance The sfBalance field value. + * @param publicKey The sfPublicKey field value. + * @param settleDelay The sfSettleDelay field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + PayChannelBuilder(std::decay_t const& account,std::decay_t const& destination,std::decay_t const& amount,std::decay_t const& balance,std::decay_t const& publicKey,std::decay_t const& settleDelay,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltPAYCHAN) + { + setAccount(account); + setDestination(destination); + setAmount(amount); + setBalance(balance); + setPublicKey(publicKey); + setSettleDelay(settleDelay); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a PayChannelBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + PayChannelBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltPAYCHAN) + { + throw std::runtime_error("Invalid ledger entry type for PayChannel"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfBalance (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setBalance(std::decay_t const& value) + { + object_[sfBalance] = value; + return *this; + } + + /** + * @brief Set sfPublicKey (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setPublicKey(std::decay_t const& value) + { + object_[sfPublicKey] = value; + return *this; + } + + /** + * @brief Set sfSettleDelay (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setSettleDelay(std::decay_t const& value) + { + object_[sfSettleDelay] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfCancelAfter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setCancelAfter(std::decay_t const& value) + { + object_[sfCancelAfter] = value; + return *this; + } + + /** + * @brief Set sfSourceTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setSourceTag(std::decay_t const& value) + { + object_[sfSourceTag] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfDestinationNode (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setDestinationNode(std::decay_t const& value) + { + object_[sfDestinationNode] = value; + return *this; + } + + /** + * @brief Build and return the completed PayChannel wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + PayChannel + build(uint256 const& index) + { + return PayChannel{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h b/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h new file mode 100644 index 0000000000..630f46517f --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h @@ -0,0 +1,240 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class PermissionedDomainBuilder; + +/** + * @brief Ledger Entry: PermissionedDomain + * + * Type: ltPERMISSIONED_DOMAIN (0x0082) + * RPC Name: permissioned_domain + * + * Immutable wrapper around SLE providing type-safe field access. + * Use PermissionedDomainBuilder to construct new ledger entries. + */ +class PermissionedDomain : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltPERMISSIONED_DOMAIN; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for PermissionedDomain"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfOwner (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOwner() const + { + return this->sle_->at(sfOwner); + } + + /** + * @brief Get sfSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSequence() const + { + return this->sle_->at(sfSequence); + } + + /** + * @brief Get sfAcceptedCredentials (soeREQUIRED) + * @note This is an untyped field (unknown). + * @return The field value. + */ + [[nodiscard]] + STArray const& + getAcceptedCredentials() const + { + return this->sle_->getFieldArray(sfAcceptedCredentials); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for PermissionedDomain ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class PermissionedDomainBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new PermissionedDomainBuilder with required fields. + * @param owner The sfOwner field value. + * @param sequence The sfSequence field value. + * @param acceptedCredentials The sfAcceptedCredentials field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + PermissionedDomainBuilder(std::decay_t const& owner,std::decay_t const& sequence,STArray const& acceptedCredentials,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltPERMISSIONED_DOMAIN) + { + setOwner(owner); + setSequence(sequence); + setAcceptedCredentials(acceptedCredentials); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a PermissionedDomainBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + PermissionedDomainBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltPERMISSIONED_DOMAIN) + { + throw std::runtime_error("Invalid ledger entry type for PermissionedDomain"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfOwner (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfAcceptedCredentials (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainBuilder& + setAcceptedCredentials(STArray const& value) + { + object_.setFieldArray(sfAcceptedCredentials, value); + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed PermissionedDomain wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + PermissionedDomain + build(uint256 const& index) + { + return PermissionedDomain{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/RippleState.h b/include/xrpl/protocol_autogen/ledger_entries/RippleState.h new file mode 100644 index 0000000000..993496d38f --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/RippleState.h @@ -0,0 +1,425 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class RippleStateBuilder; + +/** + * @brief Ledger Entry: RippleState + * + * Type: ltRIPPLE_STATE (0x0072) + * RPC Name: state + * + * Immutable wrapper around SLE providing type-safe field access. + * Use RippleStateBuilder to construct new ledger entries. + */ +class RippleState : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltRIPPLE_STATE; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for RippleState"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfBalance (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getBalance() const + { + return this->sle_->at(sfBalance); + } + + /** + * @brief Get sfLowLimit (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getLowLimit() const + { + return this->sle_->at(sfLowLimit); + } + + /** + * @brief Get sfHighLimit (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getHighLimit() const + { + return this->sle_->at(sfHighLimit); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfLowNode (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLowNode() const + { + if (hasLowNode()) + return this->sle_->at(sfLowNode); + return std::nullopt; + } + + /** + * @brief Check if sfLowNode is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLowNode() const + { + return this->sle_->isFieldPresent(sfLowNode); + } + + /** + * @brief Get sfLowQualityIn (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLowQualityIn() const + { + if (hasLowQualityIn()) + return this->sle_->at(sfLowQualityIn); + return std::nullopt; + } + + /** + * @brief Check if sfLowQualityIn is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLowQualityIn() const + { + return this->sle_->isFieldPresent(sfLowQualityIn); + } + + /** + * @brief Get sfLowQualityOut (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLowQualityOut() const + { + if (hasLowQualityOut()) + return this->sle_->at(sfLowQualityOut); + return std::nullopt; + } + + /** + * @brief Check if sfLowQualityOut is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLowQualityOut() const + { + return this->sle_->isFieldPresent(sfLowQualityOut); + } + + /** + * @brief Get sfHighNode (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getHighNode() const + { + if (hasHighNode()) + return this->sle_->at(sfHighNode); + return std::nullopt; + } + + /** + * @brief Check if sfHighNode is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasHighNode() const + { + return this->sle_->isFieldPresent(sfHighNode); + } + + /** + * @brief Get sfHighQualityIn (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getHighQualityIn() const + { + if (hasHighQualityIn()) + return this->sle_->at(sfHighQualityIn); + return std::nullopt; + } + + /** + * @brief Check if sfHighQualityIn is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasHighQualityIn() const + { + return this->sle_->isFieldPresent(sfHighQualityIn); + } + + /** + * @brief Get sfHighQualityOut (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getHighQualityOut() const + { + if (hasHighQualityOut()) + return this->sle_->at(sfHighQualityOut); + return std::nullopt; + } + + /** + * @brief Check if sfHighQualityOut is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasHighQualityOut() const + { + return this->sle_->isFieldPresent(sfHighQualityOut); + } +}; + +/** + * @brief Builder for RippleState ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class RippleStateBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new RippleStateBuilder with required fields. + * @param balance The sfBalance field value. + * @param lowLimit The sfLowLimit field value. + * @param highLimit The sfHighLimit field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + RippleStateBuilder(std::decay_t const& balance,std::decay_t const& lowLimit,std::decay_t const& highLimit,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltRIPPLE_STATE) + { + setBalance(balance); + setLowLimit(lowLimit); + setHighLimit(highLimit); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a RippleStateBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + RippleStateBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltRIPPLE_STATE) + { + throw std::runtime_error("Invalid ledger entry type for RippleState"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfBalance (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setBalance(std::decay_t const& value) + { + object_[sfBalance] = value; + return *this; + } + + /** + * @brief Set sfLowLimit (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setLowLimit(std::decay_t const& value) + { + object_[sfLowLimit] = value; + return *this; + } + + /** + * @brief Set sfHighLimit (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setHighLimit(std::decay_t const& value) + { + object_[sfHighLimit] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfLowNode (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setLowNode(std::decay_t const& value) + { + object_[sfLowNode] = value; + return *this; + } + + /** + * @brief Set sfLowQualityIn (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setLowQualityIn(std::decay_t const& value) + { + object_[sfLowQualityIn] = value; + return *this; + } + + /** + * @brief Set sfLowQualityOut (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setLowQualityOut(std::decay_t const& value) + { + object_[sfLowQualityOut] = value; + return *this; + } + + /** + * @brief Set sfHighNode (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setHighNode(std::decay_t const& value) + { + object_[sfHighNode] = value; + return *this; + } + + /** + * @brief Set sfHighQualityIn (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setHighQualityIn(std::decay_t const& value) + { + object_[sfHighQualityIn] = value; + return *this; + } + + /** + * @brief Set sfHighQualityOut (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setHighQualityOut(std::decay_t const& value) + { + object_[sfHighQualityOut] = value; + return *this; + } + + /** + * @brief Build and return the completed RippleState wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + RippleState + build(uint256 const& index) + { + return RippleState{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/SignerList.h b/include/xrpl/protocol_autogen/ledger_entries/SignerList.h new file mode 100644 index 0000000000..57d706bc28 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/SignerList.h @@ -0,0 +1,275 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class SignerListBuilder; + +/** + * @brief Ledger Entry: SignerList + * + * Type: ltSIGNER_LIST (0x0053) + * RPC Name: signer_list + * + * Immutable wrapper around SLE providing type-safe field access. + * Use SignerListBuilder to construct new ledger entries. + */ +class SignerList : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltSIGNER_LIST; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for SignerList"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfOwner (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOwner() const + { + if (hasOwner()) + return this->sle_->at(sfOwner); + return std::nullopt; + } + + /** + * @brief Check if sfOwner is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOwner() const + { + return this->sle_->isFieldPresent(sfOwner); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfSignerQuorum (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSignerQuorum() const + { + return this->sle_->at(sfSignerQuorum); + } + + /** + * @brief Get sfSignerEntries (soeREQUIRED) + * @note This is an untyped field (unknown). + * @return The field value. + */ + [[nodiscard]] + STArray const& + getSignerEntries() const + { + return this->sle_->getFieldArray(sfSignerEntries); + } + + /** + * @brief Get sfSignerListID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSignerListID() const + { + return this->sle_->at(sfSignerListID); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for SignerList ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class SignerListBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new SignerListBuilder with required fields. + * @param ownerNode The sfOwnerNode field value. + * @param signerQuorum The sfSignerQuorum field value. + * @param signerEntries The sfSignerEntries field value. + * @param signerListID The sfSignerListID field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + SignerListBuilder(std::decay_t const& ownerNode,std::decay_t const& signerQuorum,STArray const& signerEntries,std::decay_t const& signerListID,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltSIGNER_LIST) + { + setOwnerNode(ownerNode); + setSignerQuorum(signerQuorum); + setSignerEntries(signerEntries); + setSignerListID(signerListID); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a SignerListBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + SignerListBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltSIGNER_LIST) + { + throw std::runtime_error("Invalid ledger entry type for SignerList"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfOwner (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SignerListBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + SignerListBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfSignerQuorum (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + SignerListBuilder& + setSignerQuorum(std::decay_t const& value) + { + object_[sfSignerQuorum] = value; + return *this; + } + + /** + * @brief Set sfSignerEntries (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + SignerListBuilder& + setSignerEntries(STArray const& value) + { + object_.setFieldArray(sfSignerEntries, value); + return *this; + } + + /** + * @brief Set sfSignerListID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + SignerListBuilder& + setSignerListID(std::decay_t const& value) + { + object_[sfSignerListID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + SignerListBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + SignerListBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed SignerList wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + SignerList + build(uint256 const& index) + { + return SignerList{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Ticket.h b/include/xrpl/protocol_autogen/ledger_entries/Ticket.h new file mode 100644 index 0000000000..f13e8a398f --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Ticket.h @@ -0,0 +1,215 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class TicketBuilder; + +/** + * @brief Ledger Entry: Ticket + * + * Type: ltTICKET (0x0054) + * RPC Name: ticket + * + * Immutable wrapper around SLE providing type-safe field access. + * Use TicketBuilder to construct new ledger entries. + */ +class Ticket : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltTICKET; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Ticket"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfTicketSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getTicketSequence() const + { + return this->sle_->at(sfTicketSequence); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for Ticket ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class TicketBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new TicketBuilder with required fields. + * @param account The sfAccount field value. + * @param ownerNode The sfOwnerNode field value. + * @param ticketSequence The sfTicketSequence field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + TicketBuilder(std::decay_t const& account,std::decay_t const& ownerNode,std::decay_t const& ticketSequence,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltTICKET) + { + setAccount(account); + setOwnerNode(ownerNode); + setTicketSequence(ticketSequence); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a TicketBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + TicketBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltTICKET) + { + throw std::runtime_error("Invalid ledger entry type for Ticket"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + TicketBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + TicketBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfTicketSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + TicketBuilder& + setTicketSequence(std::decay_t const& value) + { + object_[sfTicketSequence] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + TicketBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + TicketBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed Ticket wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Ticket + build(uint256 const& index) + { + return Ticket{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Vault.h b/include/xrpl/protocol_autogen/ledger_entries/Vault.h new file mode 100644 index 0000000000..e24e73fab3 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Vault.h @@ -0,0 +1,521 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class VaultBuilder; + +/** + * @brief Ledger Entry: Vault + * + * Type: ltVAULT (0x0084) + * RPC Name: vault + * + * Immutable wrapper around SLE providing type-safe field access. + * Use VaultBuilder to construct new ledger entries. + */ +class Vault : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltVAULT; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Vault"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSequence() const + { + return this->sle_->at(sfSequence); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfOwner (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOwner() const + { + return this->sle_->at(sfOwner); + } + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfData (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getData() const + { + if (hasData()) + return this->sle_->at(sfData); + return std::nullopt; + } + + /** + * @brief Check if sfData is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasData() const + { + return this->sle_->isFieldPresent(sfData); + } + + /** + * @brief Get sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->sle_->at(sfAsset); + } + + /** + * @brief Get sfAssetsTotal (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetsTotal() const + { + if (hasAssetsTotal()) + return this->sle_->at(sfAssetsTotal); + return std::nullopt; + } + + /** + * @brief Check if sfAssetsTotal is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetsTotal() const + { + return this->sle_->isFieldPresent(sfAssetsTotal); + } + + /** + * @brief Get sfAssetsAvailable (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetsAvailable() const + { + if (hasAssetsAvailable()) + return this->sle_->at(sfAssetsAvailable); + return std::nullopt; + } + + /** + * @brief Check if sfAssetsAvailable is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetsAvailable() const + { + return this->sle_->isFieldPresent(sfAssetsAvailable); + } + + /** + * @brief Get sfAssetsMaximum (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetsMaximum() const + { + if (hasAssetsMaximum()) + return this->sle_->at(sfAssetsMaximum); + return std::nullopt; + } + + /** + * @brief Check if sfAssetsMaximum is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetsMaximum() const + { + return this->sle_->isFieldPresent(sfAssetsMaximum); + } + + /** + * @brief Get sfLossUnrealized (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLossUnrealized() const + { + if (hasLossUnrealized()) + return this->sle_->at(sfLossUnrealized); + return std::nullopt; + } + + /** + * @brief Check if sfLossUnrealized is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLossUnrealized() const + { + return this->sle_->isFieldPresent(sfLossUnrealized); + } + + /** + * @brief Get sfShareMPTID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT192::type::value_type + getShareMPTID() const + { + return this->sle_->at(sfShareMPTID); + } + + /** + * @brief Get sfWithdrawalPolicy (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT8::type::value_type + getWithdrawalPolicy() const + { + return this->sle_->at(sfWithdrawalPolicy); + } + + /** + * @brief Get sfScale (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getScale() const + { + if (hasScale()) + return this->sle_->at(sfScale); + return std::nullopt; + } + + /** + * @brief Check if sfScale is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasScale() const + { + return this->sle_->isFieldPresent(sfScale); + } +}; + +/** + * @brief Builder for Vault ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class VaultBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new VaultBuilder with required fields. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + * @param sequence The sfSequence field value. + * @param ownerNode The sfOwnerNode field value. + * @param owner The sfOwner field value. + * @param account The sfAccount field value. + * @param asset The sfAsset field value. + * @param shareMPTID The sfShareMPTID field value. + * @param withdrawalPolicy The sfWithdrawalPolicy field value. + */ + VaultBuilder(std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq,std::decay_t const& sequence,std::decay_t const& ownerNode,std::decay_t const& owner,std::decay_t const& account,std::decay_t const& asset,std::decay_t const& shareMPTID,std::decay_t const& withdrawalPolicy) + : LedgerEntryBuilderBase(ltVAULT) + { + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + setSequence(sequence); + setOwnerNode(ownerNode); + setOwner(owner); + setAccount(account); + setAsset(asset); + setShareMPTID(shareMPTID); + setWithdrawalPolicy(withdrawalPolicy); + } + + /** + * @brief Construct a VaultBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + VaultBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltVAULT) + { + throw std::runtime_error("Invalid ledger entry type for Vault"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfOwner (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfData (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAssetsTotal (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setAssetsTotal(std::decay_t const& value) + { + object_[sfAssetsTotal] = value; + return *this; + } + + /** + * @brief Set sfAssetsAvailable (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setAssetsAvailable(std::decay_t const& value) + { + object_[sfAssetsAvailable] = value; + return *this; + } + + /** + * @brief Set sfAssetsMaximum (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setAssetsMaximum(std::decay_t const& value) + { + object_[sfAssetsMaximum] = value; + return *this; + } + + /** + * @brief Set sfLossUnrealized (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setLossUnrealized(std::decay_t const& value) + { + object_[sfLossUnrealized] = value; + return *this; + } + + /** + * @brief Set sfShareMPTID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setShareMPTID(std::decay_t const& value) + { + object_[sfShareMPTID] = value; + return *this; + } + + /** + * @brief Set sfWithdrawalPolicy (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setWithdrawalPolicy(std::decay_t const& value) + { + object_[sfWithdrawalPolicy] = value; + return *this; + } + + /** + * @brief Set sfScale (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setScale(std::decay_t const& value) + { + object_[sfScale] = value; + return *this; + } + + /** + * @brief Build and return the completed Vault wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Vault + build(uint256 const& index) + { + return Vault{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h new file mode 100644 index 0000000000..3309228951 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h @@ -0,0 +1,312 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class XChainOwnedClaimIDBuilder; + +/** + * @brief Ledger Entry: XChainOwnedClaimID + * + * Type: ltXCHAIN_OWNED_CLAIM_ID (0x0071) + * RPC Name: xchain_owned_claim_id + * + * Immutable wrapper around SLE providing type-safe field access. + * Use XChainOwnedClaimIDBuilder to construct new ledger entries. + */ +class XChainOwnedClaimID : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltXCHAIN_OWNED_CLAIM_ID; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for XChainOwnedClaimID"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->sle_->at(sfXChainBridge); + } + + /** + * @brief Get sfXChainClaimID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainClaimID() const + { + return this->sle_->at(sfXChainClaimID); + } + + /** + * @brief Get sfOtherChainSource (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOtherChainSource() const + { + return this->sle_->at(sfOtherChainSource); + } + + /** + * @brief Get sfXChainClaimAttestations (soeREQUIRED) + * @note This is an untyped field (unknown). + * @return The field value. + */ + [[nodiscard]] + STArray const& + getXChainClaimAttestations() const + { + return this->sle_->getFieldArray(sfXChainClaimAttestations); + } + + /** + * @brief Get sfSignatureReward (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSignatureReward() const + { + return this->sle_->at(sfSignatureReward); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for XChainOwnedClaimID ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class XChainOwnedClaimIDBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new XChainOwnedClaimIDBuilder with required fields. + * @param account The sfAccount field value. + * @param xChainBridge The sfXChainBridge field value. + * @param xChainClaimID The sfXChainClaimID field value. + * @param otherChainSource The sfOtherChainSource field value. + * @param xChainClaimAttestations The sfXChainClaimAttestations field value. + * @param signatureReward The sfSignatureReward field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + XChainOwnedClaimIDBuilder(std::decay_t const& account,std::decay_t const& xChainBridge,std::decay_t const& xChainClaimID,std::decay_t const& otherChainSource,STArray const& xChainClaimAttestations,std::decay_t const& signatureReward,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltXCHAIN_OWNED_CLAIM_ID) + { + setAccount(account); + setXChainBridge(xChainBridge); + setXChainClaimID(xChainClaimID); + setOtherChainSource(otherChainSource); + setXChainClaimAttestations(xChainClaimAttestations); + setSignatureReward(signatureReward); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a XChainOwnedClaimIDBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + XChainOwnedClaimIDBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltXCHAIN_OWNED_CLAIM_ID) + { + throw std::runtime_error("Invalid ledger entry type for XChainOwnedClaimID"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfXChainClaimID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setXChainClaimID(std::decay_t const& value) + { + object_[sfXChainClaimID] = value; + return *this; + } + + /** + * @brief Set sfOtherChainSource (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setOtherChainSource(std::decay_t const& value) + { + object_[sfOtherChainSource] = value; + return *this; + } + + /** + * @brief Set sfXChainClaimAttestations (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setXChainClaimAttestations(STArray const& value) + { + object_.setFieldArray(sfXChainClaimAttestations, value); + return *this; + } + + /** + * @brief Set sfSignatureReward (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setSignatureReward(std::decay_t const& value) + { + object_[sfSignatureReward] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed XChainOwnedClaimID wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + XChainOwnedClaimID + build(uint256 const& index) + { + return XChainOwnedClaimID{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h new file mode 100644 index 0000000000..5415d52884 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h @@ -0,0 +1,264 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class XChainOwnedCreateAccountClaimIDBuilder; + +/** + * @brief Ledger Entry: XChainOwnedCreateAccountClaimID + * + * Type: ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID (0x0074) + * RPC Name: xchain_owned_create_account_claim_id + * + * Immutable wrapper around SLE providing type-safe field access. + * Use XChainOwnedCreateAccountClaimIDBuilder to construct new ledger entries. + */ +class XChainOwnedCreateAccountClaimID : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for XChainOwnedCreateAccountClaimID"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->sle_->at(sfXChainBridge); + } + + /** + * @brief Get sfXChainAccountCreateCount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainAccountCreateCount() const + { + return this->sle_->at(sfXChainAccountCreateCount); + } + + /** + * @brief Get sfXChainCreateAccountAttestations (soeREQUIRED) + * @note This is an untyped field (unknown). + * @return The field value. + */ + [[nodiscard]] + STArray const& + getXChainCreateAccountAttestations() const + { + return this->sle_->getFieldArray(sfXChainCreateAccountAttestations); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for XChainOwnedCreateAccountClaimID ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class XChainOwnedCreateAccountClaimIDBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new XChainOwnedCreateAccountClaimIDBuilder with required fields. + * @param account The sfAccount field value. + * @param xChainBridge The sfXChainBridge field value. + * @param xChainAccountCreateCount The sfXChainAccountCreateCount field value. + * @param xChainCreateAccountAttestations The sfXChainCreateAccountAttestations field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + XChainOwnedCreateAccountClaimIDBuilder(std::decay_t const& account,std::decay_t const& xChainBridge,std::decay_t const& xChainAccountCreateCount,STArray const& xChainCreateAccountAttestations,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID) + { + setAccount(account); + setXChainBridge(xChainBridge); + setXChainAccountCreateCount(xChainAccountCreateCount); + setXChainCreateAccountAttestations(xChainCreateAccountAttestations); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a XChainOwnedCreateAccountClaimIDBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + XChainOwnedCreateAccountClaimIDBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID) + { + throw std::runtime_error("Invalid ledger entry type for XChainOwnedCreateAccountClaimID"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedCreateAccountClaimIDBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedCreateAccountClaimIDBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfXChainAccountCreateCount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedCreateAccountClaimIDBuilder& + setXChainAccountCreateCount(std::decay_t const& value) + { + object_[sfXChainAccountCreateCount] = value; + return *this; + } + + /** + * @brief Set sfXChainCreateAccountAttestations (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedCreateAccountClaimIDBuilder& + setXChainCreateAccountAttestations(STArray const& value) + { + object_.setFieldArray(sfXChainCreateAccountAttestations, value); + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedCreateAccountClaimIDBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedCreateAccountClaimIDBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedCreateAccountClaimIDBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed XChainOwnedCreateAccountClaimID wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + XChainOwnedCreateAccountClaimID + build(uint256 const& index) + { + return XChainOwnedCreateAccountClaimID{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/transactions/AMMBid.h b/include/xrpl/protocol_autogen/transactions/AMMBid.h new file mode 100644 index 0000000000..d3f8cba8f7 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AMMBid.h @@ -0,0 +1,262 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AMMBidBuilder; + +/** + * @brief Transaction: AMMBid + * + * Type: ttAMM_BID (39) + * Delegable: Delegation::delegable + * Amendment: featureAMM + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AMMBidBuilder to construct new transactions. + */ +class AMMBid : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMM_BID; + + /** + * @brief Construct a AMMBid transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AMMBid(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AMMBid"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->tx_->at(sfAsset); + } + + /** + * @brief Get sfAsset2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset2() const + { + return this->tx_->at(sfAsset2); + } + + /** + * @brief Get sfBidMin (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBidMin() const + { + if (hasBidMin()) + { + return this->tx_->at(sfBidMin); + } + return std::nullopt; + } + + /** + * @brief Check if sfBidMin is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBidMin() const + { + return this->tx_->isFieldPresent(sfBidMin); + } + + /** + * @brief Get sfBidMax (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBidMax() const + { + if (hasBidMax()) + { + return this->tx_->at(sfBidMax); + } + return std::nullopt; + } + + /** + * @brief Check if sfBidMax is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBidMax() const + { + return this->tx_->isFieldPresent(sfBidMax); + } + /** + * @brief Get sfAuthAccounts (soeOPTIONAL) + * @note This is an untyped field. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getAuthAccounts() const + { + if (this->tx_->isFieldPresent(sfAuthAccounts)) + return this->tx_->getFieldArray(sfAuthAccounts); + return std::nullopt; + } + + /** + * @brief Check if sfAuthAccounts is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuthAccounts() const + { + return this->tx_->isFieldPresent(sfAuthAccounts); + } +}; + +/** + * @brief Builder for AMMBid transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AMMBidBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AMMBidBuilder with required fields. + * @param account The account initiating the transaction. + * @param asset The sfAsset field value. + * @param asset2 The sfAsset2 field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AMMBidBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& asset, std::decay_t const& asset2, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMM_BID, account, sequence, fee) + { + setAsset(asset); + setAsset2(asset2); + } + + /** + * @brief Construct a AMMBidBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AMMBidBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMM_BID) + { + throw std::runtime_error("Invalid transaction type for AMMBidBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMBidBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAsset2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMBidBuilder& + setAsset2(std::decay_t const& value) + { + object_[sfAsset2] = STIssue(sfAsset2, value); + return *this; + } + + /** + * @brief Set sfBidMin (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMBidBuilder& + setBidMin(std::decay_t const& value) + { + object_[sfBidMin] = value; + return *this; + } + + /** + * @brief Set sfBidMax (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMBidBuilder& + setBidMax(std::decay_t const& value) + { + object_[sfBidMax] = value; + return *this; + } + + /** + * @brief Set sfAuthAccounts (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMBidBuilder& + setAuthAccounts(STArray const& value) + { + object_.setFieldArray(sfAuthAccounts, value); + return *this; + } + + /** + * @brief Build and return the AMMBid wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AMMBid + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AMMBid{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AMMClawback.h b/include/xrpl/protocol_autogen/transactions/AMMClawback.h new file mode 100644 index 0000000000..9603cad6ef --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AMMClawback.h @@ -0,0 +1,214 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AMMClawbackBuilder; + +/** + * @brief Transaction: AMMClawback + * + * Type: ttAMM_CLAWBACK (31) + * Delegable: Delegation::delegable + * Amendment: featureAMMClawback + * Privileges: mayDeleteAcct | overrideFreeze + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AMMClawbackBuilder to construct new transactions. + */ +class AMMClawback : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMM_CLAWBACK; + + /** + * @brief Construct a AMMClawback transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AMMClawback(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AMMClawback"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfHolder (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getHolder() const + { + return this->tx_->at(sfHolder); + } + + /** + * @brief Get sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->tx_->at(sfAsset); + } + + /** + * @brief Get sfAsset2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset2() const + { + return this->tx_->at(sfAsset2); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } +}; + +/** + * @brief Builder for AMMClawback transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AMMClawbackBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AMMClawbackBuilder with required fields. + * @param account The account initiating the transaction. + * @param holder The sfHolder field value. + * @param asset The sfAsset field value. + * @param asset2 The sfAsset2 field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AMMClawbackBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& holder, std::decay_t const& asset, std::decay_t const& asset2, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMM_CLAWBACK, account, sequence, fee) + { + setHolder(holder); + setAsset(asset); + setAsset2(asset2); + } + + /** + * @brief Construct a AMMClawbackBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AMMClawbackBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMM_CLAWBACK) + { + throw std::runtime_error("Invalid transaction type for AMMClawbackBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfHolder (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMClawbackBuilder& + setHolder(std::decay_t const& value) + { + object_[sfHolder] = value; + return *this; + } + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMClawbackBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAsset2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMClawbackBuilder& + setAsset2(std::decay_t const& value) + { + object_[sfAsset2] = STIssue(sfAsset2, value); + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMClawbackBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Build and return the AMMClawback wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AMMClawback + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AMMClawback{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AMMCreate.h b/include/xrpl/protocol_autogen/transactions/AMMCreate.h new file mode 100644 index 0000000000..311d19a3c4 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AMMCreate.h @@ -0,0 +1,177 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AMMCreateBuilder; + +/** + * @brief Transaction: AMMCreate + * + * Type: ttAMM_CREATE (35) + * Delegable: Delegation::delegable + * Amendment: featureAMM + * Privileges: createPseudoAcct + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AMMCreateBuilder to construct new transactions. + */ +class AMMCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMM_CREATE; + + /** + * @brief Construct a AMMCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AMMCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AMMCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfAmount2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount2() const + { + return this->tx_->at(sfAmount2); + } + + /** + * @brief Get sfTradingFee (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT16::type::value_type + getTradingFee() const + { + return this->tx_->at(sfTradingFee); + } +}; + +/** + * @brief Builder for AMMCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AMMCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AMMCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param amount The sfAmount field value. + * @param amount2 The sfAmount2 field value. + * @param tradingFee The sfTradingFee field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AMMCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& amount, std::decay_t const& amount2, std::decay_t const& tradingFee, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMM_CREATE, account, sequence, fee) + { + setAmount(amount); + setAmount2(amount2); + setTradingFee(tradingFee); + } + + /** + * @brief Construct a AMMCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AMMCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMM_CREATE) + { + throw std::runtime_error("Invalid transaction type for AMMCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMCreateBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfAmount2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMCreateBuilder& + setAmount2(std::decay_t const& value) + { + object_[sfAmount2] = value; + return *this; + } + + /** + * @brief Set sfTradingFee (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMCreateBuilder& + setTradingFee(std::decay_t const& value) + { + object_[sfTradingFee] = value; + return *this; + } + + /** + * @brief Build and return the AMMCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AMMCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AMMCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AMMDelete.h b/include/xrpl/protocol_autogen/transactions/AMMDelete.h new file mode 100644 index 0000000000..bc61434d0b --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AMMDelete.h @@ -0,0 +1,153 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AMMDeleteBuilder; + +/** + * @brief Transaction: AMMDelete + * + * Type: ttAMM_DELETE (40) + * Delegable: Delegation::delegable + * Amendment: featureAMM + * Privileges: mustDeleteAcct + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AMMDeleteBuilder to construct new transactions. + */ +class AMMDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMM_DELETE; + + /** + * @brief Construct a AMMDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AMMDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AMMDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->tx_->at(sfAsset); + } + + /** + * @brief Get sfAsset2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset2() const + { + return this->tx_->at(sfAsset2); + } +}; + +/** + * @brief Builder for AMMDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AMMDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AMMDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param asset The sfAsset field value. + * @param asset2 The sfAsset2 field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AMMDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& asset, std::decay_t const& asset2, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMM_DELETE, account, sequence, fee) + { + setAsset(asset); + setAsset2(asset2); + } + + /** + * @brief Construct a AMMDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AMMDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMM_DELETE) + { + throw std::runtime_error("Invalid transaction type for AMMDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMDeleteBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAsset2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMDeleteBuilder& + setAsset2(std::decay_t const& value) + { + object_[sfAsset2] = STIssue(sfAsset2, value); + return *this; + } + + /** + * @brief Build and return the AMMDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AMMDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AMMDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AMMDeposit.h b/include/xrpl/protocol_autogen/transactions/AMMDeposit.h new file mode 100644 index 0000000000..8e86339b0a --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AMMDeposit.h @@ -0,0 +1,338 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AMMDepositBuilder; + +/** + * @brief Transaction: AMMDeposit + * + * Type: ttAMM_DEPOSIT (36) + * Delegable: Delegation::delegable + * Amendment: featureAMM + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AMMDepositBuilder to construct new transactions. + */ +class AMMDeposit : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMM_DEPOSIT; + + /** + * @brief Construct a AMMDeposit transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AMMDeposit(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AMMDeposit"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->tx_->at(sfAsset); + } + + /** + * @brief Get sfAsset2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset2() const + { + return this->tx_->at(sfAsset2); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } + + /** + * @brief Get sfAmount2 (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount2() const + { + if (hasAmount2()) + { + return this->tx_->at(sfAmount2); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount2 is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount2() const + { + return this->tx_->isFieldPresent(sfAmount2); + } + + /** + * @brief Get sfEPrice (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getEPrice() const + { + if (hasEPrice()) + { + return this->tx_->at(sfEPrice); + } + return std::nullopt; + } + + /** + * @brief Check if sfEPrice is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasEPrice() const + { + return this->tx_->isFieldPresent(sfEPrice); + } + + /** + * @brief Get sfLPTokenOut (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLPTokenOut() const + { + if (hasLPTokenOut()) + { + return this->tx_->at(sfLPTokenOut); + } + return std::nullopt; + } + + /** + * @brief Check if sfLPTokenOut is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLPTokenOut() const + { + return this->tx_->isFieldPresent(sfLPTokenOut); + } + + /** + * @brief Get sfTradingFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTradingFee() const + { + if (hasTradingFee()) + { + return this->tx_->at(sfTradingFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfTradingFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTradingFee() const + { + return this->tx_->isFieldPresent(sfTradingFee); + } +}; + +/** + * @brief Builder for AMMDeposit transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AMMDepositBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AMMDepositBuilder with required fields. + * @param account The account initiating the transaction. + * @param asset The sfAsset field value. + * @param asset2 The sfAsset2 field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AMMDepositBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& asset, std::decay_t const& asset2, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMM_DEPOSIT, account, sequence, fee) + { + setAsset(asset); + setAsset2(asset2); + } + + /** + * @brief Construct a AMMDepositBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AMMDepositBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMM_DEPOSIT) + { + throw std::runtime_error("Invalid transaction type for AMMDepositBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMDepositBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAsset2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMDepositBuilder& + setAsset2(std::decay_t const& value) + { + object_[sfAsset2] = STIssue(sfAsset2, value); + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMDepositBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfAmount2 (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMDepositBuilder& + setAmount2(std::decay_t const& value) + { + object_[sfAmount2] = value; + return *this; + } + + /** + * @brief Set sfEPrice (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMDepositBuilder& + setEPrice(std::decay_t const& value) + { + object_[sfEPrice] = value; + return *this; + } + + /** + * @brief Set sfLPTokenOut (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMDepositBuilder& + setLPTokenOut(std::decay_t const& value) + { + object_[sfLPTokenOut] = value; + return *this; + } + + /** + * @brief Set sfTradingFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMDepositBuilder& + setTradingFee(std::decay_t const& value) + { + object_[sfTradingFee] = value; + return *this; + } + + /** + * @brief Build and return the AMMDeposit wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AMMDeposit + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AMMDeposit{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AMMVote.h b/include/xrpl/protocol_autogen/transactions/AMMVote.h new file mode 100644 index 0000000000..a4e58c9aa4 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AMMVote.h @@ -0,0 +1,177 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AMMVoteBuilder; + +/** + * @brief Transaction: AMMVote + * + * Type: ttAMM_VOTE (38) + * Delegable: Delegation::delegable + * Amendment: featureAMM + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AMMVoteBuilder to construct new transactions. + */ +class AMMVote : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMM_VOTE; + + /** + * @brief Construct a AMMVote transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AMMVote(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AMMVote"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->tx_->at(sfAsset); + } + + /** + * @brief Get sfAsset2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset2() const + { + return this->tx_->at(sfAsset2); + } + + /** + * @brief Get sfTradingFee (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT16::type::value_type + getTradingFee() const + { + return this->tx_->at(sfTradingFee); + } +}; + +/** + * @brief Builder for AMMVote transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AMMVoteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AMMVoteBuilder with required fields. + * @param account The account initiating the transaction. + * @param asset The sfAsset field value. + * @param asset2 The sfAsset2 field value. + * @param tradingFee The sfTradingFee field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AMMVoteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& asset, std::decay_t const& asset2, std::decay_t const& tradingFee, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMM_VOTE, account, sequence, fee) + { + setAsset(asset); + setAsset2(asset2); + setTradingFee(tradingFee); + } + + /** + * @brief Construct a AMMVoteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AMMVoteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMM_VOTE) + { + throw std::runtime_error("Invalid transaction type for AMMVoteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMVoteBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAsset2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMVoteBuilder& + setAsset2(std::decay_t const& value) + { + object_[sfAsset2] = STIssue(sfAsset2, value); + return *this; + } + + /** + * @brief Set sfTradingFee (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMVoteBuilder& + setTradingFee(std::decay_t const& value) + { + object_[sfTradingFee] = value; + return *this; + } + + /** + * @brief Build and return the AMMVote wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AMMVote + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AMMVote{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AMMWithdraw.h b/include/xrpl/protocol_autogen/transactions/AMMWithdraw.h new file mode 100644 index 0000000000..da19c546ee --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AMMWithdraw.h @@ -0,0 +1,301 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AMMWithdrawBuilder; + +/** + * @brief Transaction: AMMWithdraw + * + * Type: ttAMM_WITHDRAW (37) + * Delegable: Delegation::delegable + * Amendment: featureAMM + * Privileges: mayDeleteAcct + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AMMWithdrawBuilder to construct new transactions. + */ +class AMMWithdraw : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMM_WITHDRAW; + + /** + * @brief Construct a AMMWithdraw transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AMMWithdraw(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AMMWithdraw"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->tx_->at(sfAsset); + } + + /** + * @brief Get sfAsset2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset2() const + { + return this->tx_->at(sfAsset2); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } + + /** + * @brief Get sfAmount2 (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount2() const + { + if (hasAmount2()) + { + return this->tx_->at(sfAmount2); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount2 is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount2() const + { + return this->tx_->isFieldPresent(sfAmount2); + } + + /** + * @brief Get sfEPrice (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getEPrice() const + { + if (hasEPrice()) + { + return this->tx_->at(sfEPrice); + } + return std::nullopt; + } + + /** + * @brief Check if sfEPrice is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasEPrice() const + { + return this->tx_->isFieldPresent(sfEPrice); + } + + /** + * @brief Get sfLPTokenIn (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLPTokenIn() const + { + if (hasLPTokenIn()) + { + return this->tx_->at(sfLPTokenIn); + } + return std::nullopt; + } + + /** + * @brief Check if sfLPTokenIn is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLPTokenIn() const + { + return this->tx_->isFieldPresent(sfLPTokenIn); + } +}; + +/** + * @brief Builder for AMMWithdraw transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AMMWithdrawBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AMMWithdrawBuilder with required fields. + * @param account The account initiating the transaction. + * @param asset The sfAsset field value. + * @param asset2 The sfAsset2 field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AMMWithdrawBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& asset, std::decay_t const& asset2, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMM_WITHDRAW, account, sequence, fee) + { + setAsset(asset); + setAsset2(asset2); + } + + /** + * @brief Construct a AMMWithdrawBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AMMWithdrawBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMM_WITHDRAW) + { + throw std::runtime_error("Invalid transaction type for AMMWithdrawBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMWithdrawBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAsset2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMWithdrawBuilder& + setAsset2(std::decay_t const& value) + { + object_[sfAsset2] = STIssue(sfAsset2, value); + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMWithdrawBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfAmount2 (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMWithdrawBuilder& + setAmount2(std::decay_t const& value) + { + object_[sfAmount2] = value; + return *this; + } + + /** + * @brief Set sfEPrice (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMWithdrawBuilder& + setEPrice(std::decay_t const& value) + { + object_[sfEPrice] = value; + return *this; + } + + /** + * @brief Set sfLPTokenIn (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMWithdrawBuilder& + setLPTokenIn(std::decay_t const& value) + { + object_[sfLPTokenIn] = value; + return *this; + } + + /** + * @brief Build and return the AMMWithdraw wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AMMWithdraw + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AMMWithdraw{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AccountDelete.h b/include/xrpl/protocol_autogen/transactions/AccountDelete.h new file mode 100644 index 0000000000..86ae9af546 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AccountDelete.h @@ -0,0 +1,203 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AccountDeleteBuilder; + +/** + * @brief Transaction: AccountDelete + * + * Type: ttACCOUNT_DELETE (21) + * Delegable: Delegation::notDelegable + * Amendment: uint256{} + * Privileges: mustDeleteAcct + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AccountDeleteBuilder to construct new transactions. + */ +class AccountDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttACCOUNT_DELETE; + + /** + * @brief Construct a AccountDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AccountDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AccountDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationTag() const + { + if (hasDestinationTag()) + { + return this->tx_->at(sfDestinationTag); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestinationTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestinationTag() const + { + return this->tx_->isFieldPresent(sfDestinationTag); + } + + /** + * @brief Get sfCredentialIDs (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCredentialIDs() const + { + if (hasCredentialIDs()) + { + return this->tx_->at(sfCredentialIDs); + } + return std::nullopt; + } + + /** + * @brief Check if sfCredentialIDs is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCredentialIDs() const + { + return this->tx_->isFieldPresent(sfCredentialIDs); + } +}; + +/** + * @brief Builder for AccountDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AccountDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AccountDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param destination The sfDestination field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AccountDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& destination, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttACCOUNT_DELETE, account, sequence, fee) + { + setDestination(destination); + } + + /** + * @brief Construct a AccountDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AccountDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttACCOUNT_DELETE) + { + throw std::runtime_error("Invalid transaction type for AccountDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AccountDeleteBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountDeleteBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Set sfCredentialIDs (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountDeleteBuilder& + setCredentialIDs(std::decay_t const& value) + { + object_[sfCredentialIDs] = value; + return *this; + } + + /** + * @brief Build and return the AccountDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AccountDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AccountDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AccountSet.h b/include/xrpl/protocol_autogen/transactions/AccountSet.h new file mode 100644 index 0000000000..c00142bd1e --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AccountSet.h @@ -0,0 +1,475 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AccountSetBuilder; + +/** + * @brief Transaction: AccountSet + * + * Type: ttACCOUNT_SET (3) + * Delegable: Delegation::notDelegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AccountSetBuilder to construct new transactions. + */ +class AccountSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttACCOUNT_SET; + + /** + * @brief Construct a AccountSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AccountSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AccountSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfEmailHash (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getEmailHash() const + { + if (hasEmailHash()) + { + return this->tx_->at(sfEmailHash); + } + return std::nullopt; + } + + /** + * @brief Check if sfEmailHash is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasEmailHash() const + { + return this->tx_->isFieldPresent(sfEmailHash); + } + + /** + * @brief Get sfWalletLocator (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getWalletLocator() const + { + if (hasWalletLocator()) + { + return this->tx_->at(sfWalletLocator); + } + return std::nullopt; + } + + /** + * @brief Check if sfWalletLocator is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasWalletLocator() const + { + return this->tx_->isFieldPresent(sfWalletLocator); + } + + /** + * @brief Get sfWalletSize (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getWalletSize() const + { + if (hasWalletSize()) + { + return this->tx_->at(sfWalletSize); + } + return std::nullopt; + } + + /** + * @brief Check if sfWalletSize is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasWalletSize() const + { + return this->tx_->isFieldPresent(sfWalletSize); + } + + /** + * @brief Get sfMessageKey (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMessageKey() const + { + if (hasMessageKey()) + { + return this->tx_->at(sfMessageKey); + } + return std::nullopt; + } + + /** + * @brief Check if sfMessageKey is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMessageKey() const + { + return this->tx_->isFieldPresent(sfMessageKey); + } + + /** + * @brief Get sfDomain (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomain() const + { + if (hasDomain()) + { + return this->tx_->at(sfDomain); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomain is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomain() const + { + return this->tx_->isFieldPresent(sfDomain); + } + + /** + * @brief Get sfTransferRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTransferRate() const + { + if (hasTransferRate()) + { + return this->tx_->at(sfTransferRate); + } + return std::nullopt; + } + + /** + * @brief Check if sfTransferRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTransferRate() const + { + return this->tx_->isFieldPresent(sfTransferRate); + } + + /** + * @brief Get sfSetFlag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSetFlag() const + { + if (hasSetFlag()) + { + return this->tx_->at(sfSetFlag); + } + return std::nullopt; + } + + /** + * @brief Check if sfSetFlag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSetFlag() const + { + return this->tx_->isFieldPresent(sfSetFlag); + } + + /** + * @brief Get sfClearFlag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getClearFlag() const + { + if (hasClearFlag()) + { + return this->tx_->at(sfClearFlag); + } + return std::nullopt; + } + + /** + * @brief Check if sfClearFlag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasClearFlag() const + { + return this->tx_->isFieldPresent(sfClearFlag); + } + + /** + * @brief Get sfTickSize (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTickSize() const + { + if (hasTickSize()) + { + return this->tx_->at(sfTickSize); + } + return std::nullopt; + } + + /** + * @brief Check if sfTickSize is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTickSize() const + { + return this->tx_->isFieldPresent(sfTickSize); + } + + /** + * @brief Get sfNFTokenMinter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNFTokenMinter() const + { + if (hasNFTokenMinter()) + { + return this->tx_->at(sfNFTokenMinter); + } + return std::nullopt; + } + + /** + * @brief Check if sfNFTokenMinter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNFTokenMinter() const + { + return this->tx_->isFieldPresent(sfNFTokenMinter); + } +}; + +/** + * @brief Builder for AccountSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AccountSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AccountSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AccountSetBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttACCOUNT_SET, account, sequence, fee) + { + } + + /** + * @brief Construct a AccountSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AccountSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttACCOUNT_SET) + { + throw std::runtime_error("Invalid transaction type for AccountSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfEmailHash (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setEmailHash(std::decay_t const& value) + { + object_[sfEmailHash] = value; + return *this; + } + + /** + * @brief Set sfWalletLocator (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setWalletLocator(std::decay_t const& value) + { + object_[sfWalletLocator] = value; + return *this; + } + + /** + * @brief Set sfWalletSize (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setWalletSize(std::decay_t const& value) + { + object_[sfWalletSize] = value; + return *this; + } + + /** + * @brief Set sfMessageKey (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setMessageKey(std::decay_t const& value) + { + object_[sfMessageKey] = value; + return *this; + } + + /** + * @brief Set sfDomain (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setDomain(std::decay_t const& value) + { + object_[sfDomain] = value; + return *this; + } + + /** + * @brief Set sfTransferRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setTransferRate(std::decay_t const& value) + { + object_[sfTransferRate] = value; + return *this; + } + + /** + * @brief Set sfSetFlag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setSetFlag(std::decay_t const& value) + { + object_[sfSetFlag] = value; + return *this; + } + + /** + * @brief Set sfClearFlag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setClearFlag(std::decay_t const& value) + { + object_[sfClearFlag] = value; + return *this; + } + + /** + * @brief Set sfTickSize (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setTickSize(std::decay_t const& value) + { + object_[sfTickSize] = value; + return *this; + } + + /** + * @brief Set sfNFTokenMinter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setNFTokenMinter(std::decay_t const& value) + { + object_[sfNFTokenMinter] = value; + return *this; + } + + /** + * @brief Build and return the AccountSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AccountSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AccountSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/Batch.h b/include/xrpl/protocol_autogen/transactions/Batch.h new file mode 100644 index 0000000000..0bb638435c --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/Batch.h @@ -0,0 +1,164 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class BatchBuilder; + +/** + * @brief Transaction: Batch + * + * Type: ttBATCH (71) + * Delegable: Delegation::notDelegable + * Amendment: featureBatch + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use BatchBuilder to construct new transactions. + */ +class Batch : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttBATCH; + + /** + * @brief Construct a Batch transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit Batch(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for Batch"); + } + } + + // Transaction-specific field getters + /** + * @brief Get sfRawTransactions (soeREQUIRED) + * @note This is an untyped field. + * @return The field value. + */ + [[nodiscard]] + STArray const& + getRawTransactions() const + { + return this->tx_->getFieldArray(sfRawTransactions); + } + /** + * @brief Get sfBatchSigners (soeOPTIONAL) + * @note This is an untyped field. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getBatchSigners() const + { + if (this->tx_->isFieldPresent(sfBatchSigners)) + return this->tx_->getFieldArray(sfBatchSigners); + return std::nullopt; + } + + /** + * @brief Check if sfBatchSigners is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBatchSigners() const + { + return this->tx_->isFieldPresent(sfBatchSigners); + } +}; + +/** + * @brief Builder for Batch transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class BatchBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new BatchBuilder with required fields. + * @param account The account initiating the transaction. + * @param rawTransactions The sfRawTransactions field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + BatchBuilder(SF_ACCOUNT::type::value_type account, + STArray const& rawTransactions, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttBATCH, account, sequence, fee) + { + setRawTransactions(rawTransactions); + } + + /** + * @brief Construct a BatchBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + BatchBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttBATCH) + { + throw std::runtime_error("Invalid transaction type for BatchBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfRawTransactions (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BatchBuilder& + setRawTransactions(STArray const& value) + { + object_.setFieldArray(sfRawTransactions, value); + return *this; + } + + /** + * @brief Set sfBatchSigners (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + BatchBuilder& + setBatchSigners(STArray const& value) + { + object_.setFieldArray(sfBatchSigners, value); + return *this; + } + + /** + * @brief Build and return the Batch wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + Batch + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return Batch{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/CheckCancel.h b/include/xrpl/protocol_autogen/transactions/CheckCancel.h new file mode 100644 index 0000000000..2409e7064c --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/CheckCancel.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class CheckCancelBuilder; + +/** + * @brief Transaction: CheckCancel + * + * Type: ttCHECK_CANCEL (18) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use CheckCancelBuilder to construct new transactions. + */ +class CheckCancel : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCHECK_CANCEL; + + /** + * @brief Construct a CheckCancel transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit CheckCancel(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for CheckCancel"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfCheckID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getCheckID() const + { + return this->tx_->at(sfCheckID); + } +}; + +/** + * @brief Builder for CheckCancel transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class CheckCancelBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new CheckCancelBuilder with required fields. + * @param account The account initiating the transaction. + * @param checkID The sfCheckID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + CheckCancelBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& checkID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttCHECK_CANCEL, account, sequence, fee) + { + setCheckID(checkID); + } + + /** + * @brief Construct a CheckCancelBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + CheckCancelBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCHECK_CANCEL) + { + throw std::runtime_error("Invalid transaction type for CheckCancelBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfCheckID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckCancelBuilder& + setCheckID(std::decay_t const& value) + { + object_[sfCheckID] = value; + return *this; + } + + /** + * @brief Build and return the CheckCancel wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + CheckCancel + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return CheckCancel{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/CheckCash.h b/include/xrpl/protocol_autogen/transactions/CheckCash.h new file mode 100644 index 0000000000..2a2bc9c7af --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/CheckCash.h @@ -0,0 +1,203 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class CheckCashBuilder; + +/** + * @brief Transaction: CheckCash + * + * Type: ttCHECK_CASH (17) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use CheckCashBuilder to construct new transactions. + */ +class CheckCash : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCHECK_CASH; + + /** + * @brief Construct a CheckCash transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit CheckCash(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for CheckCash"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfCheckID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getCheckID() const + { + return this->tx_->at(sfCheckID); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } + + /** + * @brief Get sfDeliverMin (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDeliverMin() const + { + if (hasDeliverMin()) + { + return this->tx_->at(sfDeliverMin); + } + return std::nullopt; + } + + /** + * @brief Check if sfDeliverMin is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDeliverMin() const + { + return this->tx_->isFieldPresent(sfDeliverMin); + } +}; + +/** + * @brief Builder for CheckCash transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class CheckCashBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new CheckCashBuilder with required fields. + * @param account The account initiating the transaction. + * @param checkID The sfCheckID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + CheckCashBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& checkID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttCHECK_CASH, account, sequence, fee) + { + setCheckID(checkID); + } + + /** + * @brief Construct a CheckCashBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + CheckCashBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCHECK_CASH) + { + throw std::runtime_error("Invalid transaction type for CheckCashBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfCheckID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckCashBuilder& + setCheckID(std::decay_t const& value) + { + object_[sfCheckID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckCashBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfDeliverMin (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckCashBuilder& + setDeliverMin(std::decay_t const& value) + { + object_[sfDeliverMin] = value; + return *this; + } + + /** + * @brief Build and return the CheckCash wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + CheckCash + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return CheckCash{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/CheckCreate.h b/include/xrpl/protocol_autogen/transactions/CheckCreate.h new file mode 100644 index 0000000000..ba209a7fc4 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/CheckCreate.h @@ -0,0 +1,264 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class CheckCreateBuilder; + +/** + * @brief Transaction: CheckCreate + * + * Type: ttCHECK_CREATE (16) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use CheckCreateBuilder to construct new transactions. + */ +class CheckCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCHECK_CREATE; + + /** + * @brief Construct a CheckCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit CheckCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for CheckCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfSendMax (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSendMax() const + { + return this->tx_->at(sfSendMax); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + { + return this->tx_->at(sfExpiration); + } + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->tx_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationTag() const + { + if (hasDestinationTag()) + { + return this->tx_->at(sfDestinationTag); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestinationTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestinationTag() const + { + return this->tx_->isFieldPresent(sfDestinationTag); + } + + /** + * @brief Get sfInvoiceID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getInvoiceID() const + { + if (hasInvoiceID()) + { + return this->tx_->at(sfInvoiceID); + } + return std::nullopt; + } + + /** + * @brief Check if sfInvoiceID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasInvoiceID() const + { + return this->tx_->isFieldPresent(sfInvoiceID); + } +}; + +/** + * @brief Builder for CheckCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class CheckCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new CheckCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param destination The sfDestination field value. + * @param sendMax The sfSendMax field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + CheckCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& destination, std::decay_t const& sendMax, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttCHECK_CREATE, account, sequence, fee) + { + setDestination(destination); + setSendMax(sendMax); + } + + /** + * @brief Construct a CheckCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + CheckCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCHECK_CREATE) + { + throw std::runtime_error("Invalid transaction type for CheckCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckCreateBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfSendMax (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckCreateBuilder& + setSendMax(std::decay_t const& value) + { + object_[sfSendMax] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckCreateBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckCreateBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Set sfInvoiceID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckCreateBuilder& + setInvoiceID(std::decay_t const& value) + { + object_[sfInvoiceID] = value; + return *this; + } + + /** + * @brief Build and return the CheckCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + CheckCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return CheckCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/Clawback.h b/include/xrpl/protocol_autogen/transactions/Clawback.h new file mode 100644 index 0000000000..6c34ceff11 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/Clawback.h @@ -0,0 +1,168 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class ClawbackBuilder; + +/** + * @brief Transaction: Clawback + * + * Type: ttCLAWBACK (30) + * Delegable: Delegation::delegable + * Amendment: featureClawback + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use ClawbackBuilder to construct new transactions. + */ +class Clawback : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCLAWBACK; + + /** + * @brief Construct a Clawback transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit Clawback(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for Clawback"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfHolder (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getHolder() const + { + if (hasHolder()) + { + return this->tx_->at(sfHolder); + } + return std::nullopt; + } + + /** + * @brief Check if sfHolder is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasHolder() const + { + return this->tx_->isFieldPresent(sfHolder); + } +}; + +/** + * @brief Builder for Clawback transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class ClawbackBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new ClawbackBuilder with required fields. + * @param account The account initiating the transaction. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + ClawbackBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttCLAWBACK, account, sequence, fee) + { + setAmount(amount); + } + + /** + * @brief Construct a ClawbackBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + ClawbackBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCLAWBACK) + { + throw std::runtime_error("Invalid transaction type for ClawbackBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + ClawbackBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfHolder (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + ClawbackBuilder& + setHolder(std::decay_t const& value) + { + object_[sfHolder] = value; + return *this; + } + + /** + * @brief Build and return the Clawback wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + Clawback + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return Clawback{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/CredentialAccept.h b/include/xrpl/protocol_autogen/transactions/CredentialAccept.h new file mode 100644 index 0000000000..c739e4abcb --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/CredentialAccept.h @@ -0,0 +1,153 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class CredentialAcceptBuilder; + +/** + * @brief Transaction: CredentialAccept + * + * Type: ttCREDENTIAL_ACCEPT (59) + * Delegable: Delegation::delegable + * Amendment: featureCredentials + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use CredentialAcceptBuilder to construct new transactions. + */ +class CredentialAccept : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCREDENTIAL_ACCEPT; + + /** + * @brief Construct a CredentialAccept transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit CredentialAccept(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for CredentialAccept"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfIssuer (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getIssuer() const + { + return this->tx_->at(sfIssuer); + } + + /** + * @brief Get sfCredentialType (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getCredentialType() const + { + return this->tx_->at(sfCredentialType); + } +}; + +/** + * @brief Builder for CredentialAccept transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class CredentialAcceptBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new CredentialAcceptBuilder with required fields. + * @param account The account initiating the transaction. + * @param issuer The sfIssuer field value. + * @param credentialType The sfCredentialType field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + CredentialAcceptBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& issuer, std::decay_t const& credentialType, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttCREDENTIAL_ACCEPT, account, sequence, fee) + { + setIssuer(issuer); + setCredentialType(credentialType); + } + + /** + * @brief Construct a CredentialAcceptBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + CredentialAcceptBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCREDENTIAL_ACCEPT) + { + throw std::runtime_error("Invalid transaction type for CredentialAcceptBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfIssuer (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialAcceptBuilder& + setIssuer(std::decay_t const& value) + { + object_[sfIssuer] = value; + return *this; + } + + /** + * @brief Set sfCredentialType (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialAcceptBuilder& + setCredentialType(std::decay_t const& value) + { + object_[sfCredentialType] = value; + return *this; + } + + /** + * @brief Build and return the CredentialAccept wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + CredentialAccept + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return CredentialAccept{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/CredentialCreate.h b/include/xrpl/protocol_autogen/transactions/CredentialCreate.h new file mode 100644 index 0000000000..4da5b3ff47 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/CredentialCreate.h @@ -0,0 +1,227 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class CredentialCreateBuilder; + +/** + * @brief Transaction: CredentialCreate + * + * Type: ttCREDENTIAL_CREATE (58) + * Delegable: Delegation::delegable + * Amendment: featureCredentials + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use CredentialCreateBuilder to construct new transactions. + */ +class CredentialCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCREDENTIAL_CREATE; + + /** + * @brief Construct a CredentialCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit CredentialCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for CredentialCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfSubject (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getSubject() const + { + return this->tx_->at(sfSubject); + } + + /** + * @brief Get sfCredentialType (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getCredentialType() const + { + return this->tx_->at(sfCredentialType); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + { + return this->tx_->at(sfExpiration); + } + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->tx_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + { + return this->tx_->at(sfURI); + } + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->tx_->isFieldPresent(sfURI); + } +}; + +/** + * @brief Builder for CredentialCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class CredentialCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new CredentialCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param subject The sfSubject field value. + * @param credentialType The sfCredentialType field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + CredentialCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& subject, std::decay_t const& credentialType, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttCREDENTIAL_CREATE, account, sequence, fee) + { + setSubject(subject); + setCredentialType(credentialType); + } + + /** + * @brief Construct a CredentialCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + CredentialCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCREDENTIAL_CREATE) + { + throw std::runtime_error("Invalid transaction type for CredentialCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfSubject (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialCreateBuilder& + setSubject(std::decay_t const& value) + { + object_[sfSubject] = value; + return *this; + } + + /** + * @brief Set sfCredentialType (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialCreateBuilder& + setCredentialType(std::decay_t const& value) + { + object_[sfCredentialType] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CredentialCreateBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CredentialCreateBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Build and return the CredentialCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + CredentialCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return CredentialCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/CredentialDelete.h b/include/xrpl/protocol_autogen/transactions/CredentialDelete.h new file mode 100644 index 0000000000..bd840ed104 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/CredentialDelete.h @@ -0,0 +1,203 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class CredentialDeleteBuilder; + +/** + * @brief Transaction: CredentialDelete + * + * Type: ttCREDENTIAL_DELETE (60) + * Delegable: Delegation::delegable + * Amendment: featureCredentials + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use CredentialDeleteBuilder to construct new transactions. + */ +class CredentialDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCREDENTIAL_DELETE; + + /** + * @brief Construct a CredentialDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit CredentialDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for CredentialDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfSubject (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSubject() const + { + if (hasSubject()) + { + return this->tx_->at(sfSubject); + } + return std::nullopt; + } + + /** + * @brief Check if sfSubject is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSubject() const + { + return this->tx_->isFieldPresent(sfSubject); + } + + /** + * @brief Get sfIssuer (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getIssuer() const + { + if (hasIssuer()) + { + return this->tx_->at(sfIssuer); + } + return std::nullopt; + } + + /** + * @brief Check if sfIssuer is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasIssuer() const + { + return this->tx_->isFieldPresent(sfIssuer); + } + + /** + * @brief Get sfCredentialType (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getCredentialType() const + { + return this->tx_->at(sfCredentialType); + } +}; + +/** + * @brief Builder for CredentialDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class CredentialDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new CredentialDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param credentialType The sfCredentialType field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + CredentialDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& credentialType, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttCREDENTIAL_DELETE, account, sequence, fee) + { + setCredentialType(credentialType); + } + + /** + * @brief Construct a CredentialDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + CredentialDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCREDENTIAL_DELETE) + { + throw std::runtime_error("Invalid transaction type for CredentialDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfSubject (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CredentialDeleteBuilder& + setSubject(std::decay_t const& value) + { + object_[sfSubject] = value; + return *this; + } + + /** + * @brief Set sfIssuer (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CredentialDeleteBuilder& + setIssuer(std::decay_t const& value) + { + object_[sfIssuer] = value; + return *this; + } + + /** + * @brief Set sfCredentialType (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialDeleteBuilder& + setCredentialType(std::decay_t const& value) + { + object_[sfCredentialType] = value; + return *this; + } + + /** + * @brief Build and return the CredentialDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + CredentialDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return CredentialDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/DIDDelete.h b/include/xrpl/protocol_autogen/transactions/DIDDelete.h new file mode 100644 index 0000000000..c4e61eb19e --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/DIDDelete.h @@ -0,0 +1,105 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class DIDDeleteBuilder; + +/** + * @brief Transaction: DIDDelete + * + * Type: ttDID_DELETE (50) + * Delegable: Delegation::delegable + * Amendment: featureDID + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use DIDDeleteBuilder to construct new transactions. + */ +class DIDDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttDID_DELETE; + + /** + * @brief Construct a DIDDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit DIDDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for DIDDelete"); + } + } + + // Transaction-specific field getters +}; + +/** + * @brief Builder for DIDDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class DIDDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new DIDDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + DIDDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttDID_DELETE, account, sequence, fee) + { + } + + /** + * @brief Construct a DIDDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + DIDDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttDID_DELETE) + { + throw std::runtime_error("Invalid transaction type for DIDDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Build and return the DIDDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + DIDDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return DIDDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/DIDSet.h b/include/xrpl/protocol_autogen/transactions/DIDSet.h new file mode 100644 index 0000000000..33313d384c --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/DIDSet.h @@ -0,0 +1,216 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class DIDSetBuilder; + +/** + * @brief Transaction: DIDSet + * + * Type: ttDID_SET (49) + * Delegable: Delegation::delegable + * Amendment: featureDID + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use DIDSetBuilder to construct new transactions. + */ +class DIDSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttDID_SET; + + /** + * @brief Construct a DIDSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit DIDSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for DIDSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDIDDocument (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDIDDocument() const + { + if (hasDIDDocument()) + { + return this->tx_->at(sfDIDDocument); + } + return std::nullopt; + } + + /** + * @brief Check if sfDIDDocument is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDIDDocument() const + { + return this->tx_->isFieldPresent(sfDIDDocument); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + { + return this->tx_->at(sfURI); + } + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->tx_->isFieldPresent(sfURI); + } + + /** + * @brief Get sfData (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getData() const + { + if (hasData()) + { + return this->tx_->at(sfData); + } + return std::nullopt; + } + + /** + * @brief Check if sfData is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasData() const + { + return this->tx_->isFieldPresent(sfData); + } +}; + +/** + * @brief Builder for DIDSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class DIDSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new DIDSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + DIDSetBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttDID_SET, account, sequence, fee) + { + } + + /** + * @brief Construct a DIDSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + DIDSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttDID_SET) + { + throw std::runtime_error("Invalid transaction type for DIDSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDIDDocument (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DIDSetBuilder& + setDIDDocument(std::decay_t const& value) + { + object_[sfDIDDocument] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DIDSetBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Set sfData (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DIDSetBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Build and return the DIDSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + DIDSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return DIDSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/DelegateSet.h b/include/xrpl/protocol_autogen/transactions/DelegateSet.h new file mode 100644 index 0000000000..cac4b3abef --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/DelegateSet.h @@ -0,0 +1,153 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class DelegateSetBuilder; + +/** + * @brief Transaction: DelegateSet + * + * Type: ttDELEGATE_SET (64) + * Delegable: Delegation::notDelegable + * Amendment: featurePermissionDelegationV1_1 + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use DelegateSetBuilder to construct new transactions. + */ +class DelegateSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttDELEGATE_SET; + + /** + * @brief Construct a DelegateSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit DelegateSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for DelegateSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAuthorize (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAuthorize() const + { + return this->tx_->at(sfAuthorize); + } + /** + * @brief Get sfPermissions (soeREQUIRED) + * @note This is an untyped field. + * @return The field value. + */ + [[nodiscard]] + STArray const& + getPermissions() const + { + return this->tx_->getFieldArray(sfPermissions); + } +}; + +/** + * @brief Builder for DelegateSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class DelegateSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new DelegateSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param authorize The sfAuthorize field value. + * @param permissions The sfPermissions field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + DelegateSetBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& authorize, STArray const& permissions, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttDELEGATE_SET, account, sequence, fee) + { + setAuthorize(authorize); + setPermissions(permissions); + } + + /** + * @brief Construct a DelegateSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + DelegateSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttDELEGATE_SET) + { + throw std::runtime_error("Invalid transaction type for DelegateSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAuthorize (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateSetBuilder& + setAuthorize(std::decay_t const& value) + { + object_[sfAuthorize] = value; + return *this; + } + + /** + * @brief Set sfPermissions (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateSetBuilder& + setPermissions(STArray const& value) + { + object_.setFieldArray(sfPermissions, value); + return *this; + } + + /** + * @brief Build and return the DelegateSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + DelegateSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return DelegateSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/DepositPreauth.h b/include/xrpl/protocol_autogen/transactions/DepositPreauth.h new file mode 100644 index 0000000000..eae8b113a9 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/DepositPreauth.h @@ -0,0 +1,249 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class DepositPreauthBuilder; + +/** + * @brief Transaction: DepositPreauth + * + * Type: ttDEPOSIT_PREAUTH (19) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use DepositPreauthBuilder to construct new transactions. + */ +class DepositPreauth : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttDEPOSIT_PREAUTH; + + /** + * @brief Construct a DepositPreauth transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit DepositPreauth(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for DepositPreauth"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAuthorize (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAuthorize() const + { + if (hasAuthorize()) + { + return this->tx_->at(sfAuthorize); + } + return std::nullopt; + } + + /** + * @brief Check if sfAuthorize is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuthorize() const + { + return this->tx_->isFieldPresent(sfAuthorize); + } + + /** + * @brief Get sfUnauthorize (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getUnauthorize() const + { + if (hasUnauthorize()) + { + return this->tx_->at(sfUnauthorize); + } + return std::nullopt; + } + + /** + * @brief Check if sfUnauthorize is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasUnauthorize() const + { + return this->tx_->isFieldPresent(sfUnauthorize); + } + /** + * @brief Get sfAuthorizeCredentials (soeOPTIONAL) + * @note This is an untyped field. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getAuthorizeCredentials() const + { + if (this->tx_->isFieldPresent(sfAuthorizeCredentials)) + return this->tx_->getFieldArray(sfAuthorizeCredentials); + return std::nullopt; + } + + /** + * @brief Check if sfAuthorizeCredentials is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuthorizeCredentials() const + { + return this->tx_->isFieldPresent(sfAuthorizeCredentials); + } + /** + * @brief Get sfUnauthorizeCredentials (soeOPTIONAL) + * @note This is an untyped field. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getUnauthorizeCredentials() const + { + if (this->tx_->isFieldPresent(sfUnauthorizeCredentials)) + return this->tx_->getFieldArray(sfUnauthorizeCredentials); + return std::nullopt; + } + + /** + * @brief Check if sfUnauthorizeCredentials is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasUnauthorizeCredentials() const + { + return this->tx_->isFieldPresent(sfUnauthorizeCredentials); + } +}; + +/** + * @brief Builder for DepositPreauth transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class DepositPreauthBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new DepositPreauthBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + DepositPreauthBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttDEPOSIT_PREAUTH, account, sequence, fee) + { + } + + /** + * @brief Construct a DepositPreauthBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + DepositPreauthBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttDEPOSIT_PREAUTH) + { + throw std::runtime_error("Invalid transaction type for DepositPreauthBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAuthorize (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setAuthorize(std::decay_t const& value) + { + object_[sfAuthorize] = value; + return *this; + } + + /** + * @brief Set sfUnauthorize (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setUnauthorize(std::decay_t const& value) + { + object_[sfUnauthorize] = value; + return *this; + } + + /** + * @brief Set sfAuthorizeCredentials (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setAuthorizeCredentials(STArray const& value) + { + object_.setFieldArray(sfAuthorizeCredentials, value); + return *this; + } + + /** + * @brief Set sfUnauthorizeCredentials (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setUnauthorizeCredentials(STArray const& value) + { + object_.setFieldArray(sfUnauthorizeCredentials, value); + return *this; + } + + /** + * @brief Build and return the DepositPreauth wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + DepositPreauth + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return DepositPreauth{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/EnableAmendment.h b/include/xrpl/protocol_autogen/transactions/EnableAmendment.h new file mode 100644 index 0000000000..0bf3dfd575 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/EnableAmendment.h @@ -0,0 +1,153 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class EnableAmendmentBuilder; + +/** + * @brief Transaction: EnableAmendment + * + * Type: ttAMENDMENT (100) + * Delegable: Delegation::notDelegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use EnableAmendmentBuilder to construct new transactions. + */ +class EnableAmendment : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMENDMENT; + + /** + * @brief Construct a EnableAmendment transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit EnableAmendment(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for EnableAmendment"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLedgerSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getLedgerSequence() const + { + return this->tx_->at(sfLedgerSequence); + } + + /** + * @brief Get sfAmendment (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getAmendment() const + { + return this->tx_->at(sfAmendment); + } +}; + +/** + * @brief Builder for EnableAmendment transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class EnableAmendmentBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new EnableAmendmentBuilder with required fields. + * @param account The account initiating the transaction. + * @param ledgerSequence The sfLedgerSequence field value. + * @param amendment The sfAmendment field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + EnableAmendmentBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& ledgerSequence, std::decay_t const& amendment, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMENDMENT, account, sequence, fee) + { + setLedgerSequence(ledgerSequence); + setAmendment(amendment); + } + + /** + * @brief Construct a EnableAmendmentBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + EnableAmendmentBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMENDMENT) + { + throw std::runtime_error("Invalid transaction type for EnableAmendmentBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLedgerSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EnableAmendmentBuilder& + setLedgerSequence(std::decay_t const& value) + { + object_[sfLedgerSequence] = value; + return *this; + } + + /** + * @brief Set sfAmendment (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EnableAmendmentBuilder& + setAmendment(std::decay_t const& value) + { + object_[sfAmendment] = value; + return *this; + } + + /** + * @brief Build and return the EnableAmendment wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + EnableAmendment + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return EnableAmendment{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/EscrowCancel.h b/include/xrpl/protocol_autogen/transactions/EscrowCancel.h new file mode 100644 index 0000000000..a92d5b10ae --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/EscrowCancel.h @@ -0,0 +1,153 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class EscrowCancelBuilder; + +/** + * @brief Transaction: EscrowCancel + * + * Type: ttESCROW_CANCEL (4) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use EscrowCancelBuilder to construct new transactions. + */ +class EscrowCancel : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttESCROW_CANCEL; + + /** + * @brief Construct a EscrowCancel transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit EscrowCancel(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for EscrowCancel"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfOwner (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOwner() const + { + return this->tx_->at(sfOwner); + } + + /** + * @brief Get sfOfferSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getOfferSequence() const + { + return this->tx_->at(sfOfferSequence); + } +}; + +/** + * @brief Builder for EscrowCancel transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class EscrowCancelBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new EscrowCancelBuilder with required fields. + * @param account The account initiating the transaction. + * @param owner The sfOwner field value. + * @param offerSequence The sfOfferSequence field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + EscrowCancelBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& owner, std::decay_t const& offerSequence, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttESCROW_CANCEL, account, sequence, fee) + { + setOwner(owner); + setOfferSequence(offerSequence); + } + + /** + * @brief Construct a EscrowCancelBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + EscrowCancelBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttESCROW_CANCEL) + { + throw std::runtime_error("Invalid transaction type for EscrowCancelBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfOwner (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowCancelBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfOfferSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowCancelBuilder& + setOfferSequence(std::decay_t const& value) + { + object_[sfOfferSequence] = value; + return *this; + } + + /** + * @brief Build and return the EscrowCancel wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + EscrowCancel + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return EscrowCancel{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/EscrowCreate.h b/include/xrpl/protocol_autogen/transactions/EscrowCreate.h new file mode 100644 index 0000000000..32dae6cc57 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/EscrowCreate.h @@ -0,0 +1,303 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class EscrowCreateBuilder; + +/** + * @brief Transaction: EscrowCreate + * + * Type: ttESCROW_CREATE (1) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use EscrowCreateBuilder to construct new transactions. + */ +class EscrowCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttESCROW_CREATE; + + /** + * @brief Construct a EscrowCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit EscrowCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for EscrowCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfCondition (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCondition() const + { + if (hasCondition()) + { + return this->tx_->at(sfCondition); + } + return std::nullopt; + } + + /** + * @brief Check if sfCondition is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCondition() const + { + return this->tx_->isFieldPresent(sfCondition); + } + + /** + * @brief Get sfCancelAfter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCancelAfter() const + { + if (hasCancelAfter()) + { + return this->tx_->at(sfCancelAfter); + } + return std::nullopt; + } + + /** + * @brief Check if sfCancelAfter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCancelAfter() const + { + return this->tx_->isFieldPresent(sfCancelAfter); + } + + /** + * @brief Get sfFinishAfter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getFinishAfter() const + { + if (hasFinishAfter()) + { + return this->tx_->at(sfFinishAfter); + } + return std::nullopt; + } + + /** + * @brief Check if sfFinishAfter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasFinishAfter() const + { + return this->tx_->isFieldPresent(sfFinishAfter); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationTag() const + { + if (hasDestinationTag()) + { + return this->tx_->at(sfDestinationTag); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestinationTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestinationTag() const + { + return this->tx_->isFieldPresent(sfDestinationTag); + } +}; + +/** + * @brief Builder for EscrowCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class EscrowCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new EscrowCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param destination The sfDestination field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + EscrowCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& destination, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttESCROW_CREATE, account, sequence, fee) + { + setDestination(destination); + setAmount(amount); + } + + /** + * @brief Construct a EscrowCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + EscrowCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttESCROW_CREATE) + { + throw std::runtime_error("Invalid transaction type for EscrowCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowCreateBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + EscrowCreateBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfCondition (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowCreateBuilder& + setCondition(std::decay_t const& value) + { + object_[sfCondition] = value; + return *this; + } + + /** + * @brief Set sfCancelAfter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowCreateBuilder& + setCancelAfter(std::decay_t const& value) + { + object_[sfCancelAfter] = value; + return *this; + } + + /** + * @brief Set sfFinishAfter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowCreateBuilder& + setFinishAfter(std::decay_t const& value) + { + object_[sfFinishAfter] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowCreateBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Build and return the EscrowCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + EscrowCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return EscrowCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/EscrowFinish.h b/include/xrpl/protocol_autogen/transactions/EscrowFinish.h new file mode 100644 index 0000000000..20e9a089fb --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/EscrowFinish.h @@ -0,0 +1,264 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class EscrowFinishBuilder; + +/** + * @brief Transaction: EscrowFinish + * + * Type: ttESCROW_FINISH (2) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use EscrowFinishBuilder to construct new transactions. + */ +class EscrowFinish : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttESCROW_FINISH; + + /** + * @brief Construct a EscrowFinish transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit EscrowFinish(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for EscrowFinish"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfOwner (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOwner() const + { + return this->tx_->at(sfOwner); + } + + /** + * @brief Get sfOfferSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getOfferSequence() const + { + return this->tx_->at(sfOfferSequence); + } + + /** + * @brief Get sfFulfillment (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getFulfillment() const + { + if (hasFulfillment()) + { + return this->tx_->at(sfFulfillment); + } + return std::nullopt; + } + + /** + * @brief Check if sfFulfillment is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasFulfillment() const + { + return this->tx_->isFieldPresent(sfFulfillment); + } + + /** + * @brief Get sfCondition (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCondition() const + { + if (hasCondition()) + { + return this->tx_->at(sfCondition); + } + return std::nullopt; + } + + /** + * @brief Check if sfCondition is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCondition() const + { + return this->tx_->isFieldPresent(sfCondition); + } + + /** + * @brief Get sfCredentialIDs (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCredentialIDs() const + { + if (hasCredentialIDs()) + { + return this->tx_->at(sfCredentialIDs); + } + return std::nullopt; + } + + /** + * @brief Check if sfCredentialIDs is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCredentialIDs() const + { + return this->tx_->isFieldPresent(sfCredentialIDs); + } +}; + +/** + * @brief Builder for EscrowFinish transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class EscrowFinishBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new EscrowFinishBuilder with required fields. + * @param account The account initiating the transaction. + * @param owner The sfOwner field value. + * @param offerSequence The sfOfferSequence field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + EscrowFinishBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& owner, std::decay_t const& offerSequence, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttESCROW_FINISH, account, sequence, fee) + { + setOwner(owner); + setOfferSequence(offerSequence); + } + + /** + * @brief Construct a EscrowFinishBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + EscrowFinishBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttESCROW_FINISH) + { + throw std::runtime_error("Invalid transaction type for EscrowFinishBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfOwner (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowFinishBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfOfferSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowFinishBuilder& + setOfferSequence(std::decay_t const& value) + { + object_[sfOfferSequence] = value; + return *this; + } + + /** + * @brief Set sfFulfillment (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowFinishBuilder& + setFulfillment(std::decay_t const& value) + { + object_[sfFulfillment] = value; + return *this; + } + + /** + * @brief Set sfCondition (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowFinishBuilder& + setCondition(std::decay_t const& value) + { + object_[sfCondition] = value; + return *this; + } + + /** + * @brief Set sfCredentialIDs (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowFinishBuilder& + setCredentialIDs(std::decay_t const& value) + { + object_[sfCredentialIDs] = value; + return *this; + } + + /** + * @brief Build and return the EscrowFinish wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + EscrowFinish + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return EscrowFinish{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h b/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h new file mode 100644 index 0000000000..6f60b81a04 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h @@ -0,0 +1,166 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LedgerStateFixBuilder; + +/** + * @brief Transaction: LedgerStateFix + * + * Type: ttLEDGER_STATE_FIX (53) + * Delegable: Delegation::delegable + * Amendment: fixNFTokenPageLinks + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LedgerStateFixBuilder to construct new transactions. + */ +class LedgerStateFix : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLEDGER_STATE_FIX; + + /** + * @brief Construct a LedgerStateFix transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LedgerStateFix(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LedgerStateFix"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLedgerFixType (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT16::type::value_type + getLedgerFixType() const + { + return this->tx_->at(sfLedgerFixType); + } + + /** + * @brief Get sfOwner (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOwner() const + { + if (hasOwner()) + { + return this->tx_->at(sfOwner); + } + return std::nullopt; + } + + /** + * @brief Check if sfOwner is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOwner() const + { + return this->tx_->isFieldPresent(sfOwner); + } +}; + +/** + * @brief Builder for LedgerStateFix transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LedgerStateFixBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LedgerStateFixBuilder with required fields. + * @param account The account initiating the transaction. + * @param ledgerFixType The sfLedgerFixType field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LedgerStateFixBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& ledgerFixType, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLEDGER_STATE_FIX, account, sequence, fee) + { + setLedgerFixType(ledgerFixType); + } + + /** + * @brief Construct a LedgerStateFixBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LedgerStateFixBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLEDGER_STATE_FIX) + { + throw std::runtime_error("Invalid transaction type for LedgerStateFixBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLedgerFixType (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LedgerStateFixBuilder& + setLedgerFixType(std::decay_t const& value) + { + object_[sfLedgerFixType] = value; + return *this; + } + + /** + * @brief Set sfOwner (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LedgerStateFixBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Build and return the LedgerStateFix wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LedgerStateFix + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LedgerStateFix{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverClawback.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverClawback.h new file mode 100644 index 0000000000..d8a9d9d52a --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverClawback.h @@ -0,0 +1,181 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanBrokerCoverClawbackBuilder; + +/** + * @brief Transaction: LoanBrokerCoverClawback + * + * Type: ttLOAN_BROKER_COVER_CLAWBACK (78) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanBrokerCoverClawbackBuilder to construct new transactions. + */ +class LoanBrokerCoverClawback : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_BROKER_COVER_CLAWBACK; + + /** + * @brief Construct a LoanBrokerCoverClawback transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanBrokerCoverClawback(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerCoverClawback"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanBrokerID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanBrokerID() const + { + if (hasLoanBrokerID()) + { + return this->tx_->at(sfLoanBrokerID); + } + return std::nullopt; + } + + /** + * @brief Check if sfLoanBrokerID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanBrokerID() const + { + return this->tx_->isFieldPresent(sfLoanBrokerID); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } +}; + +/** + * @brief Builder for LoanBrokerCoverClawback transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanBrokerCoverClawbackBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanBrokerCoverClawbackBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanBrokerCoverClawbackBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_BROKER_COVER_CLAWBACK, account, sequence, fee) + { + } + + /** + * @brief Construct a LoanBrokerCoverClawbackBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanBrokerCoverClawbackBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_BROKER_COVER_CLAWBACK) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerCoverClawbackBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanBrokerID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverClawbackBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverClawbackBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Build and return the LoanBrokerCoverClawback wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanBrokerCoverClawback + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanBrokerCoverClawback{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverDeposit.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverDeposit.h new file mode 100644 index 0000000000..baa567a7ac --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverDeposit.h @@ -0,0 +1,155 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanBrokerCoverDepositBuilder; + +/** + * @brief Transaction: LoanBrokerCoverDeposit + * + * Type: ttLOAN_BROKER_COVER_DEPOSIT (76) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanBrokerCoverDepositBuilder to construct new transactions. + */ +class LoanBrokerCoverDeposit : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_BROKER_COVER_DEPOSIT; + + /** + * @brief Construct a LoanBrokerCoverDeposit transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanBrokerCoverDeposit(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerCoverDeposit"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanBrokerID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanBrokerID() const + { + return this->tx_->at(sfLoanBrokerID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } +}; + +/** + * @brief Builder for LoanBrokerCoverDeposit transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanBrokerCoverDepositBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanBrokerCoverDepositBuilder with required fields. + * @param account The account initiating the transaction. + * @param loanBrokerID The sfLoanBrokerID field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanBrokerCoverDepositBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& loanBrokerID, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_BROKER_COVER_DEPOSIT, account, sequence, fee) + { + setLoanBrokerID(loanBrokerID); + setAmount(amount); + } + + /** + * @brief Construct a LoanBrokerCoverDepositBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanBrokerCoverDepositBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_BROKER_COVER_DEPOSIT) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerCoverDepositBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanBrokerID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverDepositBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverDepositBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Build and return the LoanBrokerCoverDeposit wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanBrokerCoverDeposit + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanBrokerCoverDeposit{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverWithdraw.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverWithdraw.h new file mode 100644 index 0000000000..3690b6a40a --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverWithdraw.h @@ -0,0 +1,229 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanBrokerCoverWithdrawBuilder; + +/** + * @brief Transaction: LoanBrokerCoverWithdraw + * + * Type: ttLOAN_BROKER_COVER_WITHDRAW (77) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: mayAuthorizeMPT + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanBrokerCoverWithdrawBuilder to construct new transactions. + */ +class LoanBrokerCoverWithdraw : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_BROKER_COVER_WITHDRAW; + + /** + * @brief Construct a LoanBrokerCoverWithdraw transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanBrokerCoverWithdraw(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerCoverWithdraw"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanBrokerID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanBrokerID() const + { + return this->tx_->at(sfLoanBrokerID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfDestination (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestination() const + { + if (hasDestination()) + { + return this->tx_->at(sfDestination); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestination is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestination() const + { + return this->tx_->isFieldPresent(sfDestination); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationTag() const + { + if (hasDestinationTag()) + { + return this->tx_->at(sfDestinationTag); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestinationTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestinationTag() const + { + return this->tx_->isFieldPresent(sfDestinationTag); + } +}; + +/** + * @brief Builder for LoanBrokerCoverWithdraw transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanBrokerCoverWithdrawBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanBrokerCoverWithdrawBuilder with required fields. + * @param account The account initiating the transaction. + * @param loanBrokerID The sfLoanBrokerID field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanBrokerCoverWithdrawBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& loanBrokerID, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_BROKER_COVER_WITHDRAW, account, sequence, fee) + { + setLoanBrokerID(loanBrokerID); + setAmount(amount); + } + + /** + * @brief Construct a LoanBrokerCoverWithdrawBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanBrokerCoverWithdrawBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_BROKER_COVER_WITHDRAW) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerCoverWithdrawBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanBrokerID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverWithdrawBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverWithdrawBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverWithdrawBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverWithdrawBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Build and return the LoanBrokerCoverWithdraw wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanBrokerCoverWithdraw + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanBrokerCoverWithdraw{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerDelete.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerDelete.h new file mode 100644 index 0000000000..2c174ae500 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerDelete.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanBrokerDeleteBuilder; + +/** + * @brief Transaction: LoanBrokerDelete + * + * Type: ttLOAN_BROKER_DELETE (75) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: mustDeleteAcct | mayAuthorizeMPT + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanBrokerDeleteBuilder to construct new transactions. + */ +class LoanBrokerDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_BROKER_DELETE; + + /** + * @brief Construct a LoanBrokerDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanBrokerDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanBrokerID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanBrokerID() const + { + return this->tx_->at(sfLoanBrokerID); + } +}; + +/** + * @brief Builder for LoanBrokerDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanBrokerDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanBrokerDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param loanBrokerID The sfLoanBrokerID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanBrokerDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& loanBrokerID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_BROKER_DELETE, account, sequence, fee) + { + setLoanBrokerID(loanBrokerID); + } + + /** + * @brief Construct a LoanBrokerDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanBrokerDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_BROKER_DELETE) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanBrokerID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerDeleteBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Build and return the LoanBrokerDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanBrokerDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanBrokerDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerSet.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerSet.h new file mode 100644 index 0000000000..ba6ca06266 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerSet.h @@ -0,0 +1,351 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanBrokerSetBuilder; + +/** + * @brief Transaction: LoanBrokerSet + * + * Type: ttLOAN_BROKER_SET (74) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: createPseudoAcct | mayAuthorizeMPT + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanBrokerSetBuilder to construct new transactions. + */ +class LoanBrokerSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_BROKER_SET; + + /** + * @brief Construct a LoanBrokerSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanBrokerSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfVaultID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getVaultID() const + { + return this->tx_->at(sfVaultID); + } + + /** + * @brief Get sfLoanBrokerID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanBrokerID() const + { + if (hasLoanBrokerID()) + { + return this->tx_->at(sfLoanBrokerID); + } + return std::nullopt; + } + + /** + * @brief Check if sfLoanBrokerID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanBrokerID() const + { + return this->tx_->isFieldPresent(sfLoanBrokerID); + } + + /** + * @brief Get sfData (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getData() const + { + if (hasData()) + { + return this->tx_->at(sfData); + } + return std::nullopt; + } + + /** + * @brief Check if sfData is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasData() const + { + return this->tx_->isFieldPresent(sfData); + } + + /** + * @brief Get sfManagementFeeRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getManagementFeeRate() const + { + if (hasManagementFeeRate()) + { + return this->tx_->at(sfManagementFeeRate); + } + return std::nullopt; + } + + /** + * @brief Check if sfManagementFeeRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasManagementFeeRate() const + { + return this->tx_->isFieldPresent(sfManagementFeeRate); + } + + /** + * @brief Get sfDebtMaximum (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDebtMaximum() const + { + if (hasDebtMaximum()) + { + return this->tx_->at(sfDebtMaximum); + } + return std::nullopt; + } + + /** + * @brief Check if sfDebtMaximum is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDebtMaximum() const + { + return this->tx_->isFieldPresent(sfDebtMaximum); + } + + /** + * @brief Get sfCoverRateMinimum (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCoverRateMinimum() const + { + if (hasCoverRateMinimum()) + { + return this->tx_->at(sfCoverRateMinimum); + } + return std::nullopt; + } + + /** + * @brief Check if sfCoverRateMinimum is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCoverRateMinimum() const + { + return this->tx_->isFieldPresent(sfCoverRateMinimum); + } + + /** + * @brief Get sfCoverRateLiquidation (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCoverRateLiquidation() const + { + if (hasCoverRateLiquidation()) + { + return this->tx_->at(sfCoverRateLiquidation); + } + return std::nullopt; + } + + /** + * @brief Check if sfCoverRateLiquidation is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCoverRateLiquidation() const + { + return this->tx_->isFieldPresent(sfCoverRateLiquidation); + } +}; + +/** + * @brief Builder for LoanBrokerSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanBrokerSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanBrokerSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param vaultID The sfVaultID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanBrokerSetBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& vaultID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_BROKER_SET, account, sequence, fee) + { + setVaultID(vaultID); + } + + /** + * @brief Construct a LoanBrokerSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanBrokerSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_BROKER_SET) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfVaultID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerSetBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Set sfLoanBrokerID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerSetBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Set sfData (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerSetBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Set sfManagementFeeRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerSetBuilder& + setManagementFeeRate(std::decay_t const& value) + { + object_[sfManagementFeeRate] = value; + return *this; + } + + /** + * @brief Set sfDebtMaximum (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerSetBuilder& + setDebtMaximum(std::decay_t const& value) + { + object_[sfDebtMaximum] = value; + return *this; + } + + /** + * @brief Set sfCoverRateMinimum (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerSetBuilder& + setCoverRateMinimum(std::decay_t const& value) + { + object_[sfCoverRateMinimum] = value; + return *this; + } + + /** + * @brief Set sfCoverRateLiquidation (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerSetBuilder& + setCoverRateLiquidation(std::decay_t const& value) + { + object_[sfCoverRateLiquidation] = value; + return *this; + } + + /** + * @brief Build and return the LoanBrokerSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanBrokerSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanBrokerSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanDelete.h b/include/xrpl/protocol_autogen/transactions/LoanDelete.h new file mode 100644 index 0000000000..df1b49d17e --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanDelete.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanDeleteBuilder; + +/** + * @brief Transaction: LoanDelete + * + * Type: ttLOAN_DELETE (81) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanDeleteBuilder to construct new transactions. + */ +class LoanDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_DELETE; + + /** + * @brief Construct a LoanDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanID() const + { + return this->tx_->at(sfLoanID); + } +}; + +/** + * @brief Builder for LoanDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param loanID The sfLoanID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& loanID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_DELETE, account, sequence, fee) + { + setLoanID(loanID); + } + + /** + * @brief Construct a LoanDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_DELETE) + { + throw std::runtime_error("Invalid transaction type for LoanDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanDeleteBuilder& + setLoanID(std::decay_t const& value) + { + object_[sfLoanID] = value; + return *this; + } + + /** + * @brief Build and return the LoanDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanManage.h b/include/xrpl/protocol_autogen/transactions/LoanManage.h new file mode 100644 index 0000000000..3da31e4487 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanManage.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanManageBuilder; + +/** + * @brief Transaction: LoanManage + * + * Type: ttLOAN_MANAGE (82) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: mayModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanManageBuilder to construct new transactions. + */ +class LoanManage : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_MANAGE; + + /** + * @brief Construct a LoanManage transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanManage(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanManage"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanID() const + { + return this->tx_->at(sfLoanID); + } +}; + +/** + * @brief Builder for LoanManage transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanManageBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanManageBuilder with required fields. + * @param account The account initiating the transaction. + * @param loanID The sfLoanID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanManageBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& loanID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_MANAGE, account, sequence, fee) + { + setLoanID(loanID); + } + + /** + * @brief Construct a LoanManageBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanManageBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_MANAGE) + { + throw std::runtime_error("Invalid transaction type for LoanManageBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanManageBuilder& + setLoanID(std::decay_t const& value) + { + object_[sfLoanID] = value; + return *this; + } + + /** + * @brief Build and return the LoanManage wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanManage + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanManage{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanPay.h b/include/xrpl/protocol_autogen/transactions/LoanPay.h new file mode 100644 index 0000000000..e0b4376e18 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanPay.h @@ -0,0 +1,155 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanPayBuilder; + +/** + * @brief Transaction: LoanPay + * + * Type: ttLOAN_PAY (84) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: mayAuthorizeMPT | mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanPayBuilder to construct new transactions. + */ +class LoanPay : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_PAY; + + /** + * @brief Construct a LoanPay transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanPay(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanPay"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanID() const + { + return this->tx_->at(sfLoanID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } +}; + +/** + * @brief Builder for LoanPay transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanPayBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanPayBuilder with required fields. + * @param account The account initiating the transaction. + * @param loanID The sfLoanID field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanPayBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& loanID, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_PAY, account, sequence, fee) + { + setLoanID(loanID); + setAmount(amount); + } + + /** + * @brief Construct a LoanPayBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanPayBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_PAY) + { + throw std::runtime_error("Invalid transaction type for LoanPayBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanPayBuilder& + setLoanID(std::decay_t const& value) + { + object_[sfLoanID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + LoanPayBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Build and return the LoanPay wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanPay + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanPay{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanSet.h b/include/xrpl/protocol_autogen/transactions/LoanSet.h new file mode 100644 index 0000000000..7d6181f517 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanSet.h @@ -0,0 +1,706 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanSetBuilder; + +/** + * @brief Transaction: LoanSet + * + * Type: ttLOAN_SET (80) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: mayAuthorizeMPT | mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanSetBuilder to construct new transactions. + */ +class LoanSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_SET; + + /** + * @brief Construct a LoanSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanBrokerID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanBrokerID() const + { + return this->tx_->at(sfLoanBrokerID); + } + + /** + * @brief Get sfData (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getData() const + { + if (hasData()) + { + return this->tx_->at(sfData); + } + return std::nullopt; + } + + /** + * @brief Check if sfData is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasData() const + { + return this->tx_->isFieldPresent(sfData); + } + + /** + * @brief Get sfCounterparty (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCounterparty() const + { + if (hasCounterparty()) + { + return this->tx_->at(sfCounterparty); + } + return std::nullopt; + } + + /** + * @brief Check if sfCounterparty is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCounterparty() const + { + return this->tx_->isFieldPresent(sfCounterparty); + } + /** + * @brief Get sfCounterpartySignature (soeOPTIONAL) + * @note This is an untyped field. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional + getCounterpartySignature() const + { + if (this->tx_->isFieldPresent(sfCounterpartySignature)) + return this->tx_->getFieldObject(sfCounterpartySignature); + return std::nullopt; + } + + /** + * @brief Check if sfCounterpartySignature is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCounterpartySignature() const + { + return this->tx_->isFieldPresent(sfCounterpartySignature); + } + + /** + * @brief Get sfLoanOriginationFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanOriginationFee() const + { + if (hasLoanOriginationFee()) + { + return this->tx_->at(sfLoanOriginationFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfLoanOriginationFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanOriginationFee() const + { + return this->tx_->isFieldPresent(sfLoanOriginationFee); + } + + /** + * @brief Get sfLoanServiceFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanServiceFee() const + { + if (hasLoanServiceFee()) + { + return this->tx_->at(sfLoanServiceFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfLoanServiceFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanServiceFee() const + { + return this->tx_->isFieldPresent(sfLoanServiceFee); + } + + /** + * @brief Get sfLatePaymentFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLatePaymentFee() const + { + if (hasLatePaymentFee()) + { + return this->tx_->at(sfLatePaymentFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfLatePaymentFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLatePaymentFee() const + { + return this->tx_->isFieldPresent(sfLatePaymentFee); + } + + /** + * @brief Get sfClosePaymentFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getClosePaymentFee() const + { + if (hasClosePaymentFee()) + { + return this->tx_->at(sfClosePaymentFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfClosePaymentFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasClosePaymentFee() const + { + return this->tx_->isFieldPresent(sfClosePaymentFee); + } + + /** + * @brief Get sfOverpaymentFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOverpaymentFee() const + { + if (hasOverpaymentFee()) + { + return this->tx_->at(sfOverpaymentFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfOverpaymentFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOverpaymentFee() const + { + return this->tx_->isFieldPresent(sfOverpaymentFee); + } + + /** + * @brief Get sfInterestRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getInterestRate() const + { + if (hasInterestRate()) + { + return this->tx_->at(sfInterestRate); + } + return std::nullopt; + } + + /** + * @brief Check if sfInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasInterestRate() const + { + return this->tx_->isFieldPresent(sfInterestRate); + } + + /** + * @brief Get sfLateInterestRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLateInterestRate() const + { + if (hasLateInterestRate()) + { + return this->tx_->at(sfLateInterestRate); + } + return std::nullopt; + } + + /** + * @brief Check if sfLateInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLateInterestRate() const + { + return this->tx_->isFieldPresent(sfLateInterestRate); + } + + /** + * @brief Get sfCloseInterestRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCloseInterestRate() const + { + if (hasCloseInterestRate()) + { + return this->tx_->at(sfCloseInterestRate); + } + return std::nullopt; + } + + /** + * @brief Check if sfCloseInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCloseInterestRate() const + { + return this->tx_->isFieldPresent(sfCloseInterestRate); + } + + /** + * @brief Get sfOverpaymentInterestRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOverpaymentInterestRate() const + { + if (hasOverpaymentInterestRate()) + { + return this->tx_->at(sfOverpaymentInterestRate); + } + return std::nullopt; + } + + /** + * @brief Check if sfOverpaymentInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOverpaymentInterestRate() const + { + return this->tx_->isFieldPresent(sfOverpaymentInterestRate); + } + + /** + * @brief Get sfPrincipalRequested (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_NUMBER::type::value_type + getPrincipalRequested() const + { + return this->tx_->at(sfPrincipalRequested); + } + + /** + * @brief Get sfPaymentTotal (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPaymentTotal() const + { + if (hasPaymentTotal()) + { + return this->tx_->at(sfPaymentTotal); + } + return std::nullopt; + } + + /** + * @brief Check if sfPaymentTotal is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPaymentTotal() const + { + return this->tx_->isFieldPresent(sfPaymentTotal); + } + + /** + * @brief Get sfPaymentInterval (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPaymentInterval() const + { + if (hasPaymentInterval()) + { + return this->tx_->at(sfPaymentInterval); + } + return std::nullopt; + } + + /** + * @brief Check if sfPaymentInterval is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPaymentInterval() const + { + return this->tx_->isFieldPresent(sfPaymentInterval); + } + + /** + * @brief Get sfGracePeriod (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getGracePeriod() const + { + if (hasGracePeriod()) + { + return this->tx_->at(sfGracePeriod); + } + return std::nullopt; + } + + /** + * @brief Check if sfGracePeriod is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasGracePeriod() const + { + return this->tx_->isFieldPresent(sfGracePeriod); + } +}; + +/** + * @brief Builder for LoanSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param loanBrokerID The sfLoanBrokerID field value. + * @param principalRequested The sfPrincipalRequested field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanSetBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& loanBrokerID, std::decay_t const& principalRequested, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_SET, account, sequence, fee) + { + setLoanBrokerID(loanBrokerID); + setPrincipalRequested(principalRequested); + } + + /** + * @brief Construct a LoanSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_SET) + { + throw std::runtime_error("Invalid transaction type for LoanSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanBrokerID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Set sfData (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Set sfCounterparty (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setCounterparty(std::decay_t const& value) + { + object_[sfCounterparty] = value; + return *this; + } + + /** + * @brief Set sfCounterpartySignature (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setCounterpartySignature(STObject const& value) + { + object_.setFieldObject(sfCounterpartySignature, value); + return *this; + } + + /** + * @brief Set sfLoanOriginationFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setLoanOriginationFee(std::decay_t const& value) + { + object_[sfLoanOriginationFee] = value; + return *this; + } + + /** + * @brief Set sfLoanServiceFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setLoanServiceFee(std::decay_t const& value) + { + object_[sfLoanServiceFee] = value; + return *this; + } + + /** + * @brief Set sfLatePaymentFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setLatePaymentFee(std::decay_t const& value) + { + object_[sfLatePaymentFee] = value; + return *this; + } + + /** + * @brief Set sfClosePaymentFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setClosePaymentFee(std::decay_t const& value) + { + object_[sfClosePaymentFee] = value; + return *this; + } + + /** + * @brief Set sfOverpaymentFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setOverpaymentFee(std::decay_t const& value) + { + object_[sfOverpaymentFee] = value; + return *this; + } + + /** + * @brief Set sfInterestRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setInterestRate(std::decay_t const& value) + { + object_[sfInterestRate] = value; + return *this; + } + + /** + * @brief Set sfLateInterestRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setLateInterestRate(std::decay_t const& value) + { + object_[sfLateInterestRate] = value; + return *this; + } + + /** + * @brief Set sfCloseInterestRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setCloseInterestRate(std::decay_t const& value) + { + object_[sfCloseInterestRate] = value; + return *this; + } + + /** + * @brief Set sfOverpaymentInterestRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setOverpaymentInterestRate(std::decay_t const& value) + { + object_[sfOverpaymentInterestRate] = value; + return *this; + } + + /** + * @brief Set sfPrincipalRequested (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setPrincipalRequested(std::decay_t const& value) + { + object_[sfPrincipalRequested] = value; + return *this; + } + + /** + * @brief Set sfPaymentTotal (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setPaymentTotal(std::decay_t const& value) + { + object_[sfPaymentTotal] = value; + return *this; + } + + /** + * @brief Set sfPaymentInterval (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setPaymentInterval(std::decay_t const& value) + { + object_[sfPaymentInterval] = value; + return *this; + } + + /** + * @brief Set sfGracePeriod (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setGracePeriod(std::decay_t const& value) + { + object_[sfGracePeriod] = value; + return *this; + } + + /** + * @brief Build and return the LoanSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenAuthorize.h b/include/xrpl/protocol_autogen/transactions/MPTokenAuthorize.h new file mode 100644 index 0000000000..a4641f5dc0 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/MPTokenAuthorize.h @@ -0,0 +1,166 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class MPTokenAuthorizeBuilder; + +/** + * @brief Transaction: MPTokenAuthorize + * + * Type: ttMPTOKEN_AUTHORIZE (57) + * Delegable: Delegation::delegable + * Amendment: featureMPTokensV1 + * Privileges: mustAuthorizeMPT + * + * Immutable wrapper around STTx providing type-safe field access. + * Use MPTokenAuthorizeBuilder to construct new transactions. + */ +class MPTokenAuthorize : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttMPTOKEN_AUTHORIZE; + + /** + * @brief Construct a MPTokenAuthorize transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit MPTokenAuthorize(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for MPTokenAuthorize"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfMPTokenIssuanceID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT192::type::value_type + getMPTokenIssuanceID() const + { + return this->tx_->at(sfMPTokenIssuanceID); + } + + /** + * @brief Get sfHolder (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getHolder() const + { + if (hasHolder()) + { + return this->tx_->at(sfHolder); + } + return std::nullopt; + } + + /** + * @brief Check if sfHolder is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasHolder() const + { + return this->tx_->isFieldPresent(sfHolder); + } +}; + +/** + * @brief Builder for MPTokenAuthorize transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class MPTokenAuthorizeBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new MPTokenAuthorizeBuilder with required fields. + * @param account The account initiating the transaction. + * @param mPTokenIssuanceID The sfMPTokenIssuanceID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + MPTokenAuthorizeBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& mPTokenIssuanceID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttMPTOKEN_AUTHORIZE, account, sequence, fee) + { + setMPTokenIssuanceID(mPTokenIssuanceID); + } + + /** + * @brief Construct a MPTokenAuthorizeBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + MPTokenAuthorizeBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttMPTOKEN_AUTHORIZE) + { + throw std::runtime_error("Invalid transaction type for MPTokenAuthorizeBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfMPTokenIssuanceID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenAuthorizeBuilder& + setMPTokenIssuanceID(std::decay_t const& value) + { + object_[sfMPTokenIssuanceID] = value; + return *this; + } + + /** + * @brief Set sfHolder (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenAuthorizeBuilder& + setHolder(std::decay_t const& value) + { + object_[sfHolder] = value; + return *this; + } + + /** + * @brief Build and return the MPTokenAuthorize wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + MPTokenAuthorize + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return MPTokenAuthorize{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h new file mode 100644 index 0000000000..6f5c50fc4a --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h @@ -0,0 +1,327 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class MPTokenIssuanceCreateBuilder; + +/** + * @brief Transaction: MPTokenIssuanceCreate + * + * Type: ttMPTOKEN_ISSUANCE_CREATE (54) + * Delegable: Delegation::delegable + * Amendment: featureMPTokensV1 + * Privileges: createMPTIssuance + * + * Immutable wrapper around STTx providing type-safe field access. + * Use MPTokenIssuanceCreateBuilder to construct new transactions. + */ +class MPTokenIssuanceCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttMPTOKEN_ISSUANCE_CREATE; + + /** + * @brief Construct a MPTokenIssuanceCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit MPTokenIssuanceCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for MPTokenIssuanceCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAssetScale (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetScale() const + { + if (hasAssetScale()) + { + return this->tx_->at(sfAssetScale); + } + return std::nullopt; + } + + /** + * @brief Check if sfAssetScale is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetScale() const + { + return this->tx_->isFieldPresent(sfAssetScale); + } + + /** + * @brief Get sfTransferFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTransferFee() const + { + if (hasTransferFee()) + { + return this->tx_->at(sfTransferFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfTransferFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTransferFee() const + { + return this->tx_->isFieldPresent(sfTransferFee); + } + + /** + * @brief Get sfMaximumAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMaximumAmount() const + { + if (hasMaximumAmount()) + { + return this->tx_->at(sfMaximumAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfMaximumAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMaximumAmount() const + { + return this->tx_->isFieldPresent(sfMaximumAmount); + } + + /** + * @brief Get sfMPTokenMetadata (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMPTokenMetadata() const + { + if (hasMPTokenMetadata()) + { + return this->tx_->at(sfMPTokenMetadata); + } + return std::nullopt; + } + + /** + * @brief Check if sfMPTokenMetadata is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMPTokenMetadata() const + { + return this->tx_->isFieldPresent(sfMPTokenMetadata); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + { + return this->tx_->at(sfDomainID); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->tx_->isFieldPresent(sfDomainID); + } + + /** + * @brief Get sfMutableFlags (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMutableFlags() const + { + if (hasMutableFlags()) + { + return this->tx_->at(sfMutableFlags); + } + return std::nullopt; + } + + /** + * @brief Check if sfMutableFlags is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMutableFlags() const + { + return this->tx_->isFieldPresent(sfMutableFlags); + } +}; + +/** + * @brief Builder for MPTokenIssuanceCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class MPTokenIssuanceCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new MPTokenIssuanceCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + MPTokenIssuanceCreateBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttMPTOKEN_ISSUANCE_CREATE, account, sequence, fee) + { + } + + /** + * @brief Construct a MPTokenIssuanceCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + MPTokenIssuanceCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttMPTOKEN_ISSUANCE_CREATE) + { + throw std::runtime_error("Invalid transaction type for MPTokenIssuanceCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAssetScale (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceCreateBuilder& + setAssetScale(std::decay_t const& value) + { + object_[sfAssetScale] = value; + return *this; + } + + /** + * @brief Set sfTransferFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceCreateBuilder& + setTransferFee(std::decay_t const& value) + { + object_[sfTransferFee] = value; + return *this; + } + + /** + * @brief Set sfMaximumAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceCreateBuilder& + setMaximumAmount(std::decay_t const& value) + { + object_[sfMaximumAmount] = value; + return *this; + } + + /** + * @brief Set sfMPTokenMetadata (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceCreateBuilder& + setMPTokenMetadata(std::decay_t const& value) + { + object_[sfMPTokenMetadata] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceCreateBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Set sfMutableFlags (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceCreateBuilder& + setMutableFlags(std::decay_t const& value) + { + object_[sfMutableFlags] = value; + return *this; + } + + /** + * @brief Build and return the MPTokenIssuanceCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + MPTokenIssuanceCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return MPTokenIssuanceCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceDestroy.h b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceDestroy.h new file mode 100644 index 0000000000..28d2e49971 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceDestroy.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class MPTokenIssuanceDestroyBuilder; + +/** + * @brief Transaction: MPTokenIssuanceDestroy + * + * Type: ttMPTOKEN_ISSUANCE_DESTROY (55) + * Delegable: Delegation::delegable + * Amendment: featureMPTokensV1 + * Privileges: destroyMPTIssuance + * + * Immutable wrapper around STTx providing type-safe field access. + * Use MPTokenIssuanceDestroyBuilder to construct new transactions. + */ +class MPTokenIssuanceDestroy : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttMPTOKEN_ISSUANCE_DESTROY; + + /** + * @brief Construct a MPTokenIssuanceDestroy transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit MPTokenIssuanceDestroy(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for MPTokenIssuanceDestroy"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfMPTokenIssuanceID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT192::type::value_type + getMPTokenIssuanceID() const + { + return this->tx_->at(sfMPTokenIssuanceID); + } +}; + +/** + * @brief Builder for MPTokenIssuanceDestroy transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class MPTokenIssuanceDestroyBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new MPTokenIssuanceDestroyBuilder with required fields. + * @param account The account initiating the transaction. + * @param mPTokenIssuanceID The sfMPTokenIssuanceID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + MPTokenIssuanceDestroyBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& mPTokenIssuanceID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttMPTOKEN_ISSUANCE_DESTROY, account, sequence, fee) + { + setMPTokenIssuanceID(mPTokenIssuanceID); + } + + /** + * @brief Construct a MPTokenIssuanceDestroyBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + MPTokenIssuanceDestroyBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttMPTOKEN_ISSUANCE_DESTROY) + { + throw std::runtime_error("Invalid transaction type for MPTokenIssuanceDestroyBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfMPTokenIssuanceID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceDestroyBuilder& + setMPTokenIssuanceID(std::decay_t const& value) + { + object_[sfMPTokenIssuanceID] = value; + return *this; + } + + /** + * @brief Build and return the MPTokenIssuanceDestroy wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + MPTokenIssuanceDestroy + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return MPTokenIssuanceDestroy{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h new file mode 100644 index 0000000000..429b252e95 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h @@ -0,0 +1,314 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class MPTokenIssuanceSetBuilder; + +/** + * @brief Transaction: MPTokenIssuanceSet + * + * Type: ttMPTOKEN_ISSUANCE_SET (56) + * Delegable: Delegation::delegable + * Amendment: featureMPTokensV1 + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use MPTokenIssuanceSetBuilder to construct new transactions. + */ +class MPTokenIssuanceSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttMPTOKEN_ISSUANCE_SET; + + /** + * @brief Construct a MPTokenIssuanceSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit MPTokenIssuanceSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for MPTokenIssuanceSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfMPTokenIssuanceID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT192::type::value_type + getMPTokenIssuanceID() const + { + return this->tx_->at(sfMPTokenIssuanceID); + } + + /** + * @brief Get sfHolder (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getHolder() const + { + if (hasHolder()) + { + return this->tx_->at(sfHolder); + } + return std::nullopt; + } + + /** + * @brief Check if sfHolder is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasHolder() const + { + return this->tx_->isFieldPresent(sfHolder); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + { + return this->tx_->at(sfDomainID); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->tx_->isFieldPresent(sfDomainID); + } + + /** + * @brief Get sfMPTokenMetadata (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMPTokenMetadata() const + { + if (hasMPTokenMetadata()) + { + return this->tx_->at(sfMPTokenMetadata); + } + return std::nullopt; + } + + /** + * @brief Check if sfMPTokenMetadata is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMPTokenMetadata() const + { + return this->tx_->isFieldPresent(sfMPTokenMetadata); + } + + /** + * @brief Get sfTransferFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTransferFee() const + { + if (hasTransferFee()) + { + return this->tx_->at(sfTransferFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfTransferFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTransferFee() const + { + return this->tx_->isFieldPresent(sfTransferFee); + } + + /** + * @brief Get sfMutableFlags (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMutableFlags() const + { + if (hasMutableFlags()) + { + return this->tx_->at(sfMutableFlags); + } + return std::nullopt; + } + + /** + * @brief Check if sfMutableFlags is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMutableFlags() const + { + return this->tx_->isFieldPresent(sfMutableFlags); + } +}; + +/** + * @brief Builder for MPTokenIssuanceSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class MPTokenIssuanceSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new MPTokenIssuanceSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param mPTokenIssuanceID The sfMPTokenIssuanceID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + MPTokenIssuanceSetBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& mPTokenIssuanceID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttMPTOKEN_ISSUANCE_SET, account, sequence, fee) + { + setMPTokenIssuanceID(mPTokenIssuanceID); + } + + /** + * @brief Construct a MPTokenIssuanceSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + MPTokenIssuanceSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttMPTOKEN_ISSUANCE_SET) + { + throw std::runtime_error("Invalid transaction type for MPTokenIssuanceSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfMPTokenIssuanceID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceSetBuilder& + setMPTokenIssuanceID(std::decay_t const& value) + { + object_[sfMPTokenIssuanceID] = value; + return *this; + } + + /** + * @brief Set sfHolder (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceSetBuilder& + setHolder(std::decay_t const& value) + { + object_[sfHolder] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceSetBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Set sfMPTokenMetadata (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceSetBuilder& + setMPTokenMetadata(std::decay_t const& value) + { + object_[sfMPTokenMetadata] = value; + return *this; + } + + /** + * @brief Set sfTransferFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceSetBuilder& + setTransferFee(std::decay_t const& value) + { + object_[sfTransferFee] = value; + return *this; + } + + /** + * @brief Set sfMutableFlags (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceSetBuilder& + setMutableFlags(std::decay_t const& value) + { + object_[sfMutableFlags] = value; + return *this; + } + + /** + * @brief Build and return the MPTokenIssuanceSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + MPTokenIssuanceSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return MPTokenIssuanceSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenAcceptOffer.h b/include/xrpl/protocol_autogen/transactions/NFTokenAcceptOffer.h new file mode 100644 index 0000000000..53ae614ee4 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/NFTokenAcceptOffer.h @@ -0,0 +1,216 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class NFTokenAcceptOfferBuilder; + +/** + * @brief Transaction: NFTokenAcceptOffer + * + * Type: ttNFTOKEN_ACCEPT_OFFER (29) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use NFTokenAcceptOfferBuilder to construct new transactions. + */ +class NFTokenAcceptOffer : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttNFTOKEN_ACCEPT_OFFER; + + /** + * @brief Construct a NFTokenAcceptOffer transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit NFTokenAcceptOffer(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for NFTokenAcceptOffer"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfNFTokenBuyOffer (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNFTokenBuyOffer() const + { + if (hasNFTokenBuyOffer()) + { + return this->tx_->at(sfNFTokenBuyOffer); + } + return std::nullopt; + } + + /** + * @brief Check if sfNFTokenBuyOffer is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNFTokenBuyOffer() const + { + return this->tx_->isFieldPresent(sfNFTokenBuyOffer); + } + + /** + * @brief Get sfNFTokenSellOffer (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNFTokenSellOffer() const + { + if (hasNFTokenSellOffer()) + { + return this->tx_->at(sfNFTokenSellOffer); + } + return std::nullopt; + } + + /** + * @brief Check if sfNFTokenSellOffer is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNFTokenSellOffer() const + { + return this->tx_->isFieldPresent(sfNFTokenSellOffer); + } + + /** + * @brief Get sfNFTokenBrokerFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNFTokenBrokerFee() const + { + if (hasNFTokenBrokerFee()) + { + return this->tx_->at(sfNFTokenBrokerFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfNFTokenBrokerFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNFTokenBrokerFee() const + { + return this->tx_->isFieldPresent(sfNFTokenBrokerFee); + } +}; + +/** + * @brief Builder for NFTokenAcceptOffer transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class NFTokenAcceptOfferBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenAcceptOfferBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + NFTokenAcceptOfferBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttNFTOKEN_ACCEPT_OFFER, account, sequence, fee) + { + } + + /** + * @brief Construct a NFTokenAcceptOfferBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + NFTokenAcceptOfferBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttNFTOKEN_ACCEPT_OFFER) + { + throw std::runtime_error("Invalid transaction type for NFTokenAcceptOfferBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfNFTokenBuyOffer (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenAcceptOfferBuilder& + setNFTokenBuyOffer(std::decay_t const& value) + { + object_[sfNFTokenBuyOffer] = value; + return *this; + } + + /** + * @brief Set sfNFTokenSellOffer (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenAcceptOfferBuilder& + setNFTokenSellOffer(std::decay_t const& value) + { + object_[sfNFTokenSellOffer] = value; + return *this; + } + + /** + * @brief Set sfNFTokenBrokerFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenAcceptOfferBuilder& + setNFTokenBrokerFee(std::decay_t const& value) + { + object_[sfNFTokenBrokerFee] = value; + return *this; + } + + /** + * @brief Build and return the NFTokenAcceptOffer wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + NFTokenAcceptOffer + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return NFTokenAcceptOffer{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenBurn.h b/include/xrpl/protocol_autogen/transactions/NFTokenBurn.h new file mode 100644 index 0000000000..123418d00a --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/NFTokenBurn.h @@ -0,0 +1,166 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class NFTokenBurnBuilder; + +/** + * @brief Transaction: NFTokenBurn + * + * Type: ttNFTOKEN_BURN (26) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: changeNFTCounts + * + * Immutable wrapper around STTx providing type-safe field access. + * Use NFTokenBurnBuilder to construct new transactions. + */ +class NFTokenBurn : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttNFTOKEN_BURN; + + /** + * @brief Construct a NFTokenBurn transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit NFTokenBurn(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for NFTokenBurn"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfNFTokenID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getNFTokenID() const + { + return this->tx_->at(sfNFTokenID); + } + + /** + * @brief Get sfOwner (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOwner() const + { + if (hasOwner()) + { + return this->tx_->at(sfOwner); + } + return std::nullopt; + } + + /** + * @brief Check if sfOwner is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOwner() const + { + return this->tx_->isFieldPresent(sfOwner); + } +}; + +/** + * @brief Builder for NFTokenBurn transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class NFTokenBurnBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenBurnBuilder with required fields. + * @param account The account initiating the transaction. + * @param nFTokenID The sfNFTokenID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + NFTokenBurnBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& nFTokenID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttNFTOKEN_BURN, account, sequence, fee) + { + setNFTokenID(nFTokenID); + } + + /** + * @brief Construct a NFTokenBurnBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + NFTokenBurnBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttNFTOKEN_BURN) + { + throw std::runtime_error("Invalid transaction type for NFTokenBurnBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfNFTokenID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenBurnBuilder& + setNFTokenID(std::decay_t const& value) + { + object_[sfNFTokenID] = value; + return *this; + } + + /** + * @brief Set sfOwner (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenBurnBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Build and return the NFTokenBurn wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + NFTokenBurn + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return NFTokenBurn{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenCancelOffer.h b/include/xrpl/protocol_autogen/transactions/NFTokenCancelOffer.h new file mode 100644 index 0000000000..5fbbeca96c --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/NFTokenCancelOffer.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class NFTokenCancelOfferBuilder; + +/** + * @brief Transaction: NFTokenCancelOffer + * + * Type: ttNFTOKEN_CANCEL_OFFER (28) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use NFTokenCancelOfferBuilder to construct new transactions. + */ +class NFTokenCancelOffer : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttNFTOKEN_CANCEL_OFFER; + + /** + * @brief Construct a NFTokenCancelOffer transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit NFTokenCancelOffer(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for NFTokenCancelOffer"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfNFTokenOffers (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VECTOR256::type::value_type + getNFTokenOffers() const + { + return this->tx_->at(sfNFTokenOffers); + } +}; + +/** + * @brief Builder for NFTokenCancelOffer transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class NFTokenCancelOfferBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenCancelOfferBuilder with required fields. + * @param account The account initiating the transaction. + * @param nFTokenOffers The sfNFTokenOffers field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + NFTokenCancelOfferBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& nFTokenOffers, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttNFTOKEN_CANCEL_OFFER, account, sequence, fee) + { + setNFTokenOffers(nFTokenOffers); + } + + /** + * @brief Construct a NFTokenCancelOfferBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + NFTokenCancelOfferBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttNFTOKEN_CANCEL_OFFER) + { + throw std::runtime_error("Invalid transaction type for NFTokenCancelOfferBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfNFTokenOffers (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenCancelOfferBuilder& + setNFTokenOffers(std::decay_t const& value) + { + object_[sfNFTokenOffers] = value; + return *this; + } + + /** + * @brief Build and return the NFTokenCancelOffer wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + NFTokenCancelOffer + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return NFTokenCancelOffer{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenCreateOffer.h b/include/xrpl/protocol_autogen/transactions/NFTokenCreateOffer.h new file mode 100644 index 0000000000..e820272f83 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/NFTokenCreateOffer.h @@ -0,0 +1,264 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class NFTokenCreateOfferBuilder; + +/** + * @brief Transaction: NFTokenCreateOffer + * + * Type: ttNFTOKEN_CREATE_OFFER (27) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use NFTokenCreateOfferBuilder to construct new transactions. + */ +class NFTokenCreateOffer : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttNFTOKEN_CREATE_OFFER; + + /** + * @brief Construct a NFTokenCreateOffer transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit NFTokenCreateOffer(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for NFTokenCreateOffer"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfNFTokenID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getNFTokenID() const + { + return this->tx_->at(sfNFTokenID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfDestination (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestination() const + { + if (hasDestination()) + { + return this->tx_->at(sfDestination); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestination is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestination() const + { + return this->tx_->isFieldPresent(sfDestination); + } + + /** + * @brief Get sfOwner (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOwner() const + { + if (hasOwner()) + { + return this->tx_->at(sfOwner); + } + return std::nullopt; + } + + /** + * @brief Check if sfOwner is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOwner() const + { + return this->tx_->isFieldPresent(sfOwner); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + { + return this->tx_->at(sfExpiration); + } + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->tx_->isFieldPresent(sfExpiration); + } +}; + +/** + * @brief Builder for NFTokenCreateOffer transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class NFTokenCreateOfferBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenCreateOfferBuilder with required fields. + * @param account The account initiating the transaction. + * @param nFTokenID The sfNFTokenID field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + NFTokenCreateOfferBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& nFTokenID, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttNFTOKEN_CREATE_OFFER, account, sequence, fee) + { + setNFTokenID(nFTokenID); + setAmount(amount); + } + + /** + * @brief Construct a NFTokenCreateOfferBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + NFTokenCreateOfferBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttNFTOKEN_CREATE_OFFER) + { + throw std::runtime_error("Invalid transaction type for NFTokenCreateOfferBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfNFTokenID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenCreateOfferBuilder& + setNFTokenID(std::decay_t const& value) + { + object_[sfNFTokenID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenCreateOfferBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenCreateOfferBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfOwner (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenCreateOfferBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenCreateOfferBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Build and return the NFTokenCreateOffer wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + NFTokenCreateOffer + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return NFTokenCreateOffer{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenMint.h b/include/xrpl/protocol_autogen/transactions/NFTokenMint.h new file mode 100644 index 0000000000..9854147570 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/NFTokenMint.h @@ -0,0 +1,351 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class NFTokenMintBuilder; + +/** + * @brief Transaction: NFTokenMint + * + * Type: ttNFTOKEN_MINT (25) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: changeNFTCounts + * + * Immutable wrapper around STTx providing type-safe field access. + * Use NFTokenMintBuilder to construct new transactions. + */ +class NFTokenMint : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttNFTOKEN_MINT; + + /** + * @brief Construct a NFTokenMint transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit NFTokenMint(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for NFTokenMint"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfNFTokenTaxon (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getNFTokenTaxon() const + { + return this->tx_->at(sfNFTokenTaxon); + } + + /** + * @brief Get sfTransferFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTransferFee() const + { + if (hasTransferFee()) + { + return this->tx_->at(sfTransferFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfTransferFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTransferFee() const + { + return this->tx_->isFieldPresent(sfTransferFee); + } + + /** + * @brief Get sfIssuer (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getIssuer() const + { + if (hasIssuer()) + { + return this->tx_->at(sfIssuer); + } + return std::nullopt; + } + + /** + * @brief Check if sfIssuer is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasIssuer() const + { + return this->tx_->isFieldPresent(sfIssuer); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + { + return this->tx_->at(sfURI); + } + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->tx_->isFieldPresent(sfURI); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } + + /** + * @brief Get sfDestination (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestination() const + { + if (hasDestination()) + { + return this->tx_->at(sfDestination); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestination is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestination() const + { + return this->tx_->isFieldPresent(sfDestination); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + { + return this->tx_->at(sfExpiration); + } + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->tx_->isFieldPresent(sfExpiration); + } +}; + +/** + * @brief Builder for NFTokenMint transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class NFTokenMintBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenMintBuilder with required fields. + * @param account The account initiating the transaction. + * @param nFTokenTaxon The sfNFTokenTaxon field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + NFTokenMintBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& nFTokenTaxon, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttNFTOKEN_MINT, account, sequence, fee) + { + setNFTokenTaxon(nFTokenTaxon); + } + + /** + * @brief Construct a NFTokenMintBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + NFTokenMintBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttNFTOKEN_MINT) + { + throw std::runtime_error("Invalid transaction type for NFTokenMintBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfNFTokenTaxon (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenMintBuilder& + setNFTokenTaxon(std::decay_t const& value) + { + object_[sfNFTokenTaxon] = value; + return *this; + } + + /** + * @brief Set sfTransferFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenMintBuilder& + setTransferFee(std::decay_t const& value) + { + object_[sfTransferFee] = value; + return *this; + } + + /** + * @brief Set sfIssuer (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenMintBuilder& + setIssuer(std::decay_t const& value) + { + object_[sfIssuer] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenMintBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenMintBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenMintBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenMintBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Build and return the NFTokenMint wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + NFTokenMint + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return NFTokenMint{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenModify.h b/include/xrpl/protocol_autogen/transactions/NFTokenModify.h new file mode 100644 index 0000000000..6f9b75532c --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/NFTokenModify.h @@ -0,0 +1,203 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class NFTokenModifyBuilder; + +/** + * @brief Transaction: NFTokenModify + * + * Type: ttNFTOKEN_MODIFY (61) + * Delegable: Delegation::delegable + * Amendment: featureDynamicNFT + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use NFTokenModifyBuilder to construct new transactions. + */ +class NFTokenModify : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttNFTOKEN_MODIFY; + + /** + * @brief Construct a NFTokenModify transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit NFTokenModify(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for NFTokenModify"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfNFTokenID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getNFTokenID() const + { + return this->tx_->at(sfNFTokenID); + } + + /** + * @brief Get sfOwner (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOwner() const + { + if (hasOwner()) + { + return this->tx_->at(sfOwner); + } + return std::nullopt; + } + + /** + * @brief Check if sfOwner is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOwner() const + { + return this->tx_->isFieldPresent(sfOwner); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + { + return this->tx_->at(sfURI); + } + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->tx_->isFieldPresent(sfURI); + } +}; + +/** + * @brief Builder for NFTokenModify transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class NFTokenModifyBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenModifyBuilder with required fields. + * @param account The account initiating the transaction. + * @param nFTokenID The sfNFTokenID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + NFTokenModifyBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& nFTokenID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttNFTOKEN_MODIFY, account, sequence, fee) + { + setNFTokenID(nFTokenID); + } + + /** + * @brief Construct a NFTokenModifyBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + NFTokenModifyBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttNFTOKEN_MODIFY) + { + throw std::runtime_error("Invalid transaction type for NFTokenModifyBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfNFTokenID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenModifyBuilder& + setNFTokenID(std::decay_t const& value) + { + object_[sfNFTokenID] = value; + return *this; + } + + /** + * @brief Set sfOwner (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenModifyBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenModifyBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Build and return the NFTokenModify wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + NFTokenModify + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return NFTokenModify{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/OfferCancel.h b/include/xrpl/protocol_autogen/transactions/OfferCancel.h new file mode 100644 index 0000000000..ed2c761c0e --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/OfferCancel.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class OfferCancelBuilder; + +/** + * @brief Transaction: OfferCancel + * + * Type: ttOFFER_CANCEL (8) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use OfferCancelBuilder to construct new transactions. + */ +class OfferCancel : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttOFFER_CANCEL; + + /** + * @brief Construct a OfferCancel transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit OfferCancel(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for OfferCancel"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfOfferSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getOfferSequence() const + { + return this->tx_->at(sfOfferSequence); + } +}; + +/** + * @brief Builder for OfferCancel transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class OfferCancelBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new OfferCancelBuilder with required fields. + * @param account The account initiating the transaction. + * @param offerSequence The sfOfferSequence field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + OfferCancelBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& offerSequence, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttOFFER_CANCEL, account, sequence, fee) + { + setOfferSequence(offerSequence); + } + + /** + * @brief Construct a OfferCancelBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + OfferCancelBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttOFFER_CANCEL) + { + throw std::runtime_error("Invalid transaction type for OfferCancelBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfOfferSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferCancelBuilder& + setOfferSequence(std::decay_t const& value) + { + object_[sfOfferSequence] = value; + return *this; + } + + /** + * @brief Build and return the OfferCancel wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + OfferCancel + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return OfferCancel{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/OfferCreate.h b/include/xrpl/protocol_autogen/transactions/OfferCreate.h new file mode 100644 index 0000000000..a0eff385f6 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/OfferCreate.h @@ -0,0 +1,264 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class OfferCreateBuilder; + +/** + * @brief Transaction: OfferCreate + * + * Type: ttOFFER_CREATE (7) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use OfferCreateBuilder to construct new transactions. + */ +class OfferCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttOFFER_CREATE; + + /** + * @brief Construct a OfferCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit OfferCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for OfferCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfTakerPays (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getTakerPays() const + { + return this->tx_->at(sfTakerPays); + } + + /** + * @brief Get sfTakerGets (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getTakerGets() const + { + return this->tx_->at(sfTakerGets); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + { + return this->tx_->at(sfExpiration); + } + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->tx_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfOfferSequence (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOfferSequence() const + { + if (hasOfferSequence()) + { + return this->tx_->at(sfOfferSequence); + } + return std::nullopt; + } + + /** + * @brief Check if sfOfferSequence is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOfferSequence() const + { + return this->tx_->isFieldPresent(sfOfferSequence); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + { + return this->tx_->at(sfDomainID); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->tx_->isFieldPresent(sfDomainID); + } +}; + +/** + * @brief Builder for OfferCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class OfferCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new OfferCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param takerPays The sfTakerPays field value. + * @param takerGets The sfTakerGets field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + OfferCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& takerPays, std::decay_t const& takerGets, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttOFFER_CREATE, account, sequence, fee) + { + setTakerPays(takerPays); + setTakerGets(takerGets); + } + + /** + * @brief Construct a OfferCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + OfferCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttOFFER_CREATE) + { + throw std::runtime_error("Invalid transaction type for OfferCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfTakerPays (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferCreateBuilder& + setTakerPays(std::decay_t const& value) + { + object_[sfTakerPays] = value; + return *this; + } + + /** + * @brief Set sfTakerGets (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferCreateBuilder& + setTakerGets(std::decay_t const& value) + { + object_[sfTakerGets] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OfferCreateBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfOfferSequence (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OfferCreateBuilder& + setOfferSequence(std::decay_t const& value) + { + object_[sfOfferSequence] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OfferCreateBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Build and return the OfferCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + OfferCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return OfferCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/OracleDelete.h b/include/xrpl/protocol_autogen/transactions/OracleDelete.h new file mode 100644 index 0000000000..6cafd2b6de --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/OracleDelete.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class OracleDeleteBuilder; + +/** + * @brief Transaction: OracleDelete + * + * Type: ttORACLE_DELETE (52) + * Delegable: Delegation::delegable + * Amendment: featurePriceOracle + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use OracleDeleteBuilder to construct new transactions. + */ +class OracleDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttORACLE_DELETE; + + /** + * @brief Construct a OracleDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit OracleDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for OracleDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfOracleDocumentID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getOracleDocumentID() const + { + return this->tx_->at(sfOracleDocumentID); + } +}; + +/** + * @brief Builder for OracleDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class OracleDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new OracleDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param oracleDocumentID The sfOracleDocumentID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + OracleDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& oracleDocumentID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttORACLE_DELETE, account, sequence, fee) + { + setOracleDocumentID(oracleDocumentID); + } + + /** + * @brief Construct a OracleDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + OracleDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttORACLE_DELETE) + { + throw std::runtime_error("Invalid transaction type for OracleDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfOracleDocumentID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleDeleteBuilder& + setOracleDocumentID(std::decay_t const& value) + { + object_[sfOracleDocumentID] = value; + return *this; + } + + /** + * @brief Build and return the OracleDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + OracleDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return OracleDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/OracleSet.h b/include/xrpl/protocol_autogen/transactions/OracleSet.h new file mode 100644 index 0000000000..8dd0d88865 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/OracleSet.h @@ -0,0 +1,288 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class OracleSetBuilder; + +/** + * @brief Transaction: OracleSet + * + * Type: ttORACLE_SET (51) + * Delegable: Delegation::delegable + * Amendment: featurePriceOracle + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use OracleSetBuilder to construct new transactions. + */ +class OracleSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttORACLE_SET; + + /** + * @brief Construct a OracleSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit OracleSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for OracleSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfOracleDocumentID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getOracleDocumentID() const + { + return this->tx_->at(sfOracleDocumentID); + } + + /** + * @brief Get sfProvider (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getProvider() const + { + if (hasProvider()) + { + return this->tx_->at(sfProvider); + } + return std::nullopt; + } + + /** + * @brief Check if sfProvider is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasProvider() const + { + return this->tx_->isFieldPresent(sfProvider); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + { + return this->tx_->at(sfURI); + } + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->tx_->isFieldPresent(sfURI); + } + + /** + * @brief Get sfAssetClass (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetClass() const + { + if (hasAssetClass()) + { + return this->tx_->at(sfAssetClass); + } + return std::nullopt; + } + + /** + * @brief Check if sfAssetClass is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetClass() const + { + return this->tx_->isFieldPresent(sfAssetClass); + } + + /** + * @brief Get sfLastUpdateTime (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getLastUpdateTime() const + { + return this->tx_->at(sfLastUpdateTime); + } + /** + * @brief Get sfPriceDataSeries (soeREQUIRED) + * @note This is an untyped field. + * @return The field value. + */ + [[nodiscard]] + STArray const& + getPriceDataSeries() const + { + return this->tx_->getFieldArray(sfPriceDataSeries); + } +}; + +/** + * @brief Builder for OracleSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class OracleSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new OracleSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param oracleDocumentID The sfOracleDocumentID field value. + * @param lastUpdateTime The sfLastUpdateTime field value. + * @param priceDataSeries The sfPriceDataSeries field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + OracleSetBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& oracleDocumentID, std::decay_t const& lastUpdateTime, STArray const& priceDataSeries, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttORACLE_SET, account, sequence, fee) + { + setOracleDocumentID(oracleDocumentID); + setLastUpdateTime(lastUpdateTime); + setPriceDataSeries(priceDataSeries); + } + + /** + * @brief Construct a OracleSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + OracleSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttORACLE_SET) + { + throw std::runtime_error("Invalid transaction type for OracleSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfOracleDocumentID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleSetBuilder& + setOracleDocumentID(std::decay_t const& value) + { + object_[sfOracleDocumentID] = value; + return *this; + } + + /** + * @brief Set sfProvider (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OracleSetBuilder& + setProvider(std::decay_t const& value) + { + object_[sfProvider] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OracleSetBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Set sfAssetClass (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OracleSetBuilder& + setAssetClass(std::decay_t const& value) + { + object_[sfAssetClass] = value; + return *this; + } + + /** + * @brief Set sfLastUpdateTime (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleSetBuilder& + setLastUpdateTime(std::decay_t const& value) + { + object_[sfLastUpdateTime] = value; + return *this; + } + + /** + * @brief Set sfPriceDataSeries (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleSetBuilder& + setPriceDataSeries(STArray const& value) + { + object_.setFieldArray(sfPriceDataSeries, value); + return *this; + } + + /** + * @brief Build and return the OracleSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + OracleSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return OracleSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/Payment.h b/include/xrpl/protocol_autogen/transactions/Payment.h new file mode 100644 index 0000000000..877286f90e --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/Payment.h @@ -0,0 +1,416 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class PaymentBuilder; + +/** + * @brief Transaction: Payment + * + * Type: ttPAYMENT (0) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: createAcct + * + * Immutable wrapper around STTx providing type-safe field access. + * Use PaymentBuilder to construct new transactions. + */ +class Payment : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttPAYMENT; + + /** + * @brief Construct a Payment transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit Payment(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for Payment"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfSendMax (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSendMax() const + { + if (hasSendMax()) + { + return this->tx_->at(sfSendMax); + } + return std::nullopt; + } + + /** + * @brief Check if sfSendMax is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSendMax() const + { + return this->tx_->isFieldPresent(sfSendMax); + } + /** + * @brief Get sfPaths (soeDEFAULT) + * @note This is an untyped field. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getPaths() const + { + if (this->tx_->isFieldPresent(sfPaths)) + return this->tx_->getFieldPathSet(sfPaths); + return std::nullopt; + } + + /** + * @brief Check if sfPaths is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPaths() const + { + return this->tx_->isFieldPresent(sfPaths); + } + + /** + * @brief Get sfInvoiceID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getInvoiceID() const + { + if (hasInvoiceID()) + { + return this->tx_->at(sfInvoiceID); + } + return std::nullopt; + } + + /** + * @brief Check if sfInvoiceID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasInvoiceID() const + { + return this->tx_->isFieldPresent(sfInvoiceID); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationTag() const + { + if (hasDestinationTag()) + { + return this->tx_->at(sfDestinationTag); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestinationTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestinationTag() const + { + return this->tx_->isFieldPresent(sfDestinationTag); + } + + /** + * @brief Get sfDeliverMin (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDeliverMin() const + { + if (hasDeliverMin()) + { + return this->tx_->at(sfDeliverMin); + } + return std::nullopt; + } + + /** + * @brief Check if sfDeliverMin is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDeliverMin() const + { + return this->tx_->isFieldPresent(sfDeliverMin); + } + + /** + * @brief Get sfCredentialIDs (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCredentialIDs() const + { + if (hasCredentialIDs()) + { + return this->tx_->at(sfCredentialIDs); + } + return std::nullopt; + } + + /** + * @brief Check if sfCredentialIDs is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCredentialIDs() const + { + return this->tx_->isFieldPresent(sfCredentialIDs); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + { + return this->tx_->at(sfDomainID); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->tx_->isFieldPresent(sfDomainID); + } +}; + +/** + * @brief Builder for Payment transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class PaymentBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new PaymentBuilder with required fields. + * @param account The account initiating the transaction. + * @param destination The sfDestination field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + PaymentBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& destination, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttPAYMENT, account, sequence, fee) + { + setDestination(destination); + setAmount(amount); + } + + /** + * @brief Construct a PaymentBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + PaymentBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttPAYMENT) + { + throw std::runtime_error("Invalid transaction type for PaymentBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfSendMax (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setSendMax(std::decay_t const& value) + { + object_[sfSendMax] = value; + return *this; + } + + /** + * @brief Set sfPaths (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setPaths(STPathSet const& value) + { + object_.setFieldPathSet(sfPaths, value); + return *this; + } + + /** + * @brief Set sfInvoiceID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setInvoiceID(std::decay_t const& value) + { + object_[sfInvoiceID] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Set sfDeliverMin (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setDeliverMin(std::decay_t const& value) + { + object_[sfDeliverMin] = value; + return *this; + } + + /** + * @brief Set sfCredentialIDs (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setCredentialIDs(std::decay_t const& value) + { + object_[sfCredentialIDs] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Build and return the Payment wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + Payment + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return Payment{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/PaymentChannelClaim.h b/include/xrpl/protocol_autogen/transactions/PaymentChannelClaim.h new file mode 100644 index 0000000000..1e90c74aa1 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/PaymentChannelClaim.h @@ -0,0 +1,314 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class PaymentChannelClaimBuilder; + +/** + * @brief Transaction: PaymentChannelClaim + * + * Type: ttPAYCHAN_CLAIM (15) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use PaymentChannelClaimBuilder to construct new transactions. + */ +class PaymentChannelClaim : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttPAYCHAN_CLAIM; + + /** + * @brief Construct a PaymentChannelClaim transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit PaymentChannelClaim(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for PaymentChannelClaim"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfChannel (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getChannel() const + { + return this->tx_->at(sfChannel); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } + + /** + * @brief Get sfBalance (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBalance() const + { + if (hasBalance()) + { + return this->tx_->at(sfBalance); + } + return std::nullopt; + } + + /** + * @brief Check if sfBalance is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBalance() const + { + return this->tx_->isFieldPresent(sfBalance); + } + + /** + * @brief Get sfSignature (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSignature() const + { + if (hasSignature()) + { + return this->tx_->at(sfSignature); + } + return std::nullopt; + } + + /** + * @brief Check if sfSignature is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSignature() const + { + return this->tx_->isFieldPresent(sfSignature); + } + + /** + * @brief Get sfPublicKey (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPublicKey() const + { + if (hasPublicKey()) + { + return this->tx_->at(sfPublicKey); + } + return std::nullopt; + } + + /** + * @brief Check if sfPublicKey is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPublicKey() const + { + return this->tx_->isFieldPresent(sfPublicKey); + } + + /** + * @brief Get sfCredentialIDs (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCredentialIDs() const + { + if (hasCredentialIDs()) + { + return this->tx_->at(sfCredentialIDs); + } + return std::nullopt; + } + + /** + * @brief Check if sfCredentialIDs is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCredentialIDs() const + { + return this->tx_->isFieldPresent(sfCredentialIDs); + } +}; + +/** + * @brief Builder for PaymentChannelClaim transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class PaymentChannelClaimBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new PaymentChannelClaimBuilder with required fields. + * @param account The account initiating the transaction. + * @param channel The sfChannel field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + PaymentChannelClaimBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& channel, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttPAYCHAN_CLAIM, account, sequence, fee) + { + setChannel(channel); + } + + /** + * @brief Construct a PaymentChannelClaimBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + PaymentChannelClaimBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttPAYCHAN_CLAIM) + { + throw std::runtime_error("Invalid transaction type for PaymentChannelClaimBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfChannel (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentChannelClaimBuilder& + setChannel(std::decay_t const& value) + { + object_[sfChannel] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelClaimBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfBalance (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelClaimBuilder& + setBalance(std::decay_t const& value) + { + object_[sfBalance] = value; + return *this; + } + + /** + * @brief Set sfSignature (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelClaimBuilder& + setSignature(std::decay_t const& value) + { + object_[sfSignature] = value; + return *this; + } + + /** + * @brief Set sfPublicKey (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelClaimBuilder& + setPublicKey(std::decay_t const& value) + { + object_[sfPublicKey] = value; + return *this; + } + + /** + * @brief Set sfCredentialIDs (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelClaimBuilder& + setCredentialIDs(std::decay_t const& value) + { + object_[sfCredentialIDs] = value; + return *this; + } + + /** + * @brief Build and return the PaymentChannelClaim wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + PaymentChannelClaim + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return PaymentChannelClaim{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/PaymentChannelCreate.h b/include/xrpl/protocol_autogen/transactions/PaymentChannelCreate.h new file mode 100644 index 0000000000..4242046114 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/PaymentChannelCreate.h @@ -0,0 +1,275 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class PaymentChannelCreateBuilder; + +/** + * @brief Transaction: PaymentChannelCreate + * + * Type: ttPAYCHAN_CREATE (13) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use PaymentChannelCreateBuilder to construct new transactions. + */ +class PaymentChannelCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttPAYCHAN_CREATE; + + /** + * @brief Construct a PaymentChannelCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit PaymentChannelCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for PaymentChannelCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfSettleDelay (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSettleDelay() const + { + return this->tx_->at(sfSettleDelay); + } + + /** + * @brief Get sfPublicKey (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getPublicKey() const + { + return this->tx_->at(sfPublicKey); + } + + /** + * @brief Get sfCancelAfter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCancelAfter() const + { + if (hasCancelAfter()) + { + return this->tx_->at(sfCancelAfter); + } + return std::nullopt; + } + + /** + * @brief Check if sfCancelAfter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCancelAfter() const + { + return this->tx_->isFieldPresent(sfCancelAfter); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationTag() const + { + if (hasDestinationTag()) + { + return this->tx_->at(sfDestinationTag); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestinationTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestinationTag() const + { + return this->tx_->isFieldPresent(sfDestinationTag); + } +}; + +/** + * @brief Builder for PaymentChannelCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class PaymentChannelCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new PaymentChannelCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param destination The sfDestination field value. + * @param amount The sfAmount field value. + * @param settleDelay The sfSettleDelay field value. + * @param publicKey The sfPublicKey field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + PaymentChannelCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& destination, std::decay_t const& amount, std::decay_t const& settleDelay, std::decay_t const& publicKey, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttPAYCHAN_CREATE, account, sequence, fee) + { + setDestination(destination); + setAmount(amount); + setSettleDelay(settleDelay); + setPublicKey(publicKey); + } + + /** + * @brief Construct a PaymentChannelCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + PaymentChannelCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttPAYCHAN_CREATE) + { + throw std::runtime_error("Invalid transaction type for PaymentChannelCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentChannelCreateBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentChannelCreateBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfSettleDelay (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentChannelCreateBuilder& + setSettleDelay(std::decay_t const& value) + { + object_[sfSettleDelay] = value; + return *this; + } + + /** + * @brief Set sfPublicKey (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentChannelCreateBuilder& + setPublicKey(std::decay_t const& value) + { + object_[sfPublicKey] = value; + return *this; + } + + /** + * @brief Set sfCancelAfter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelCreateBuilder& + setCancelAfter(std::decay_t const& value) + { + object_[sfCancelAfter] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelCreateBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Build and return the PaymentChannelCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + PaymentChannelCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return PaymentChannelCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/PaymentChannelFund.h b/include/xrpl/protocol_autogen/transactions/PaymentChannelFund.h new file mode 100644 index 0000000000..2fecf21154 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/PaymentChannelFund.h @@ -0,0 +1,190 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class PaymentChannelFundBuilder; + +/** + * @brief Transaction: PaymentChannelFund + * + * Type: ttPAYCHAN_FUND (14) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use PaymentChannelFundBuilder to construct new transactions. + */ +class PaymentChannelFund : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttPAYCHAN_FUND; + + /** + * @brief Construct a PaymentChannelFund transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit PaymentChannelFund(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for PaymentChannelFund"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfChannel (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getChannel() const + { + return this->tx_->at(sfChannel); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + { + return this->tx_->at(sfExpiration); + } + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->tx_->isFieldPresent(sfExpiration); + } +}; + +/** + * @brief Builder for PaymentChannelFund transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class PaymentChannelFundBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new PaymentChannelFundBuilder with required fields. + * @param account The account initiating the transaction. + * @param channel The sfChannel field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + PaymentChannelFundBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& channel, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttPAYCHAN_FUND, account, sequence, fee) + { + setChannel(channel); + setAmount(amount); + } + + /** + * @brief Construct a PaymentChannelFundBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + PaymentChannelFundBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttPAYCHAN_FUND) + { + throw std::runtime_error("Invalid transaction type for PaymentChannelFundBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfChannel (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentChannelFundBuilder& + setChannel(std::decay_t const& value) + { + object_[sfChannel] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentChannelFundBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelFundBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Build and return the PaymentChannelFund wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + PaymentChannelFund + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return PaymentChannelFund{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/PermissionedDomainDelete.h b/include/xrpl/protocol_autogen/transactions/PermissionedDomainDelete.h new file mode 100644 index 0000000000..c95e0f2e18 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/PermissionedDomainDelete.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class PermissionedDomainDeleteBuilder; + +/** + * @brief Transaction: PermissionedDomainDelete + * + * Type: ttPERMISSIONED_DOMAIN_DELETE (63) + * Delegable: Delegation::delegable + * Amendment: featurePermissionedDomains + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use PermissionedDomainDeleteBuilder to construct new transactions. + */ +class PermissionedDomainDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttPERMISSIONED_DOMAIN_DELETE; + + /** + * @brief Construct a PermissionedDomainDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit PermissionedDomainDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for PermissionedDomainDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDomainID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getDomainID() const + { + return this->tx_->at(sfDomainID); + } +}; + +/** + * @brief Builder for PermissionedDomainDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class PermissionedDomainDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new PermissionedDomainDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param domainID The sfDomainID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + PermissionedDomainDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& domainID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttPERMISSIONED_DOMAIN_DELETE, account, sequence, fee) + { + setDomainID(domainID); + } + + /** + * @brief Construct a PermissionedDomainDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + PermissionedDomainDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttPERMISSIONED_DOMAIN_DELETE) + { + throw std::runtime_error("Invalid transaction type for PermissionedDomainDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDomainID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainDeleteBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Build and return the PermissionedDomainDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + PermissionedDomainDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return PermissionedDomainDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/PermissionedDomainSet.h b/include/xrpl/protocol_autogen/transactions/PermissionedDomainSet.h new file mode 100644 index 0000000000..38df7f4fc7 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/PermissionedDomainSet.h @@ -0,0 +1,166 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class PermissionedDomainSetBuilder; + +/** + * @brief Transaction: PermissionedDomainSet + * + * Type: ttPERMISSIONED_DOMAIN_SET (62) + * Delegable: Delegation::delegable + * Amendment: featurePermissionedDomains + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use PermissionedDomainSetBuilder to construct new transactions. + */ +class PermissionedDomainSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttPERMISSIONED_DOMAIN_SET; + + /** + * @brief Construct a PermissionedDomainSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit PermissionedDomainSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for PermissionedDomainSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + { + return this->tx_->at(sfDomainID); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->tx_->isFieldPresent(sfDomainID); + } + /** + * @brief Get sfAcceptedCredentials (soeREQUIRED) + * @note This is an untyped field. + * @return The field value. + */ + [[nodiscard]] + STArray const& + getAcceptedCredentials() const + { + return this->tx_->getFieldArray(sfAcceptedCredentials); + } +}; + +/** + * @brief Builder for PermissionedDomainSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class PermissionedDomainSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new PermissionedDomainSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param acceptedCredentials The sfAcceptedCredentials field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + PermissionedDomainSetBuilder(SF_ACCOUNT::type::value_type account, + STArray const& acceptedCredentials, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttPERMISSIONED_DOMAIN_SET, account, sequence, fee) + { + setAcceptedCredentials(acceptedCredentials); + } + + /** + * @brief Construct a PermissionedDomainSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + PermissionedDomainSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttPERMISSIONED_DOMAIN_SET) + { + throw std::runtime_error("Invalid transaction type for PermissionedDomainSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainSetBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Set sfAcceptedCredentials (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainSetBuilder& + setAcceptedCredentials(STArray const& value) + { + object_.setFieldArray(sfAcceptedCredentials, value); + return *this; + } + + /** + * @brief Build and return the PermissionedDomainSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + PermissionedDomainSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return PermissionedDomainSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/SetFee.h b/include/xrpl/protocol_autogen/transactions/SetFee.h new file mode 100644 index 0000000000..7bf73201b9 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/SetFee.h @@ -0,0 +1,401 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class SetFeeBuilder; + +/** + * @brief Transaction: SetFee + * + * Type: ttFEE (101) + * Delegable: Delegation::notDelegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use SetFeeBuilder to construct new transactions. + */ +class SetFee : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttFEE; + + /** + * @brief Construct a SetFee transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit SetFee(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for SetFee"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLedgerSequence (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLedgerSequence() const + { + if (hasLedgerSequence()) + { + return this->tx_->at(sfLedgerSequence); + } + return std::nullopt; + } + + /** + * @brief Check if sfLedgerSequence is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLedgerSequence() const + { + return this->tx_->isFieldPresent(sfLedgerSequence); + } + + /** + * @brief Get sfBaseFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBaseFee() const + { + if (hasBaseFee()) + { + return this->tx_->at(sfBaseFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfBaseFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBaseFee() const + { + return this->tx_->isFieldPresent(sfBaseFee); + } + + /** + * @brief Get sfReferenceFeeUnits (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReferenceFeeUnits() const + { + if (hasReferenceFeeUnits()) + { + return this->tx_->at(sfReferenceFeeUnits); + } + return std::nullopt; + } + + /** + * @brief Check if sfReferenceFeeUnits is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReferenceFeeUnits() const + { + return this->tx_->isFieldPresent(sfReferenceFeeUnits); + } + + /** + * @brief Get sfReserveBase (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveBase() const + { + if (hasReserveBase()) + { + return this->tx_->at(sfReserveBase); + } + return std::nullopt; + } + + /** + * @brief Check if sfReserveBase is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveBase() const + { + return this->tx_->isFieldPresent(sfReserveBase); + } + + /** + * @brief Get sfReserveIncrement (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveIncrement() const + { + if (hasReserveIncrement()) + { + return this->tx_->at(sfReserveIncrement); + } + return std::nullopt; + } + + /** + * @brief Check if sfReserveIncrement is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveIncrement() const + { + return this->tx_->isFieldPresent(sfReserveIncrement); + } + + /** + * @brief Get sfBaseFeeDrops (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBaseFeeDrops() const + { + if (hasBaseFeeDrops()) + { + return this->tx_->at(sfBaseFeeDrops); + } + return std::nullopt; + } + + /** + * @brief Check if sfBaseFeeDrops is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBaseFeeDrops() const + { + return this->tx_->isFieldPresent(sfBaseFeeDrops); + } + + /** + * @brief Get sfReserveBaseDrops (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveBaseDrops() const + { + if (hasReserveBaseDrops()) + { + return this->tx_->at(sfReserveBaseDrops); + } + return std::nullopt; + } + + /** + * @brief Check if sfReserveBaseDrops is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveBaseDrops() const + { + return this->tx_->isFieldPresent(sfReserveBaseDrops); + } + + /** + * @brief Get sfReserveIncrementDrops (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveIncrementDrops() const + { + if (hasReserveIncrementDrops()) + { + return this->tx_->at(sfReserveIncrementDrops); + } + return std::nullopt; + } + + /** + * @brief Check if sfReserveIncrementDrops is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveIncrementDrops() const + { + return this->tx_->isFieldPresent(sfReserveIncrementDrops); + } +}; + +/** + * @brief Builder for SetFee transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class SetFeeBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new SetFeeBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + SetFeeBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttFEE, account, sequence, fee) + { + } + + /** + * @brief Construct a SetFeeBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + SetFeeBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttFEE) + { + throw std::runtime_error("Invalid transaction type for SetFeeBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLedgerSequence (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setLedgerSequence(std::decay_t const& value) + { + object_[sfLedgerSequence] = value; + return *this; + } + + /** + * @brief Set sfBaseFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setBaseFee(std::decay_t const& value) + { + object_[sfBaseFee] = value; + return *this; + } + + /** + * @brief Set sfReferenceFeeUnits (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setReferenceFeeUnits(std::decay_t const& value) + { + object_[sfReferenceFeeUnits] = value; + return *this; + } + + /** + * @brief Set sfReserveBase (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setReserveBase(std::decay_t const& value) + { + object_[sfReserveBase] = value; + return *this; + } + + /** + * @brief Set sfReserveIncrement (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setReserveIncrement(std::decay_t const& value) + { + object_[sfReserveIncrement] = value; + return *this; + } + + /** + * @brief Set sfBaseFeeDrops (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setBaseFeeDrops(std::decay_t const& value) + { + object_[sfBaseFeeDrops] = value; + return *this; + } + + /** + * @brief Set sfReserveBaseDrops (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setReserveBaseDrops(std::decay_t const& value) + { + object_[sfReserveBaseDrops] = value; + return *this; + } + + /** + * @brief Set sfReserveIncrementDrops (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setReserveIncrementDrops(std::decay_t const& value) + { + object_[sfReserveIncrementDrops] = value; + return *this; + } + + /** + * @brief Build and return the SetFee wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + SetFee + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return SetFee{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/SetRegularKey.h b/include/xrpl/protocol_autogen/transactions/SetRegularKey.h new file mode 100644 index 0000000000..3d70d092c5 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/SetRegularKey.h @@ -0,0 +1,142 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class SetRegularKeyBuilder; + +/** + * @brief Transaction: SetRegularKey + * + * Type: ttREGULAR_KEY_SET (5) + * Delegable: Delegation::notDelegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use SetRegularKeyBuilder to construct new transactions. + */ +class SetRegularKey : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttREGULAR_KEY_SET; + + /** + * @brief Construct a SetRegularKey transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit SetRegularKey(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for SetRegularKey"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfRegularKey (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getRegularKey() const + { + if (hasRegularKey()) + { + return this->tx_->at(sfRegularKey); + } + return std::nullopt; + } + + /** + * @brief Check if sfRegularKey is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasRegularKey() const + { + return this->tx_->isFieldPresent(sfRegularKey); + } +}; + +/** + * @brief Builder for SetRegularKey transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class SetRegularKeyBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new SetRegularKeyBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + SetRegularKeyBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttREGULAR_KEY_SET, account, sequence, fee) + { + } + + /** + * @brief Construct a SetRegularKeyBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + SetRegularKeyBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttREGULAR_KEY_SET) + { + throw std::runtime_error("Invalid transaction type for SetRegularKeyBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfRegularKey (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetRegularKeyBuilder& + setRegularKey(std::decay_t const& value) + { + object_[sfRegularKey] = value; + return *this; + } + + /** + * @brief Build and return the SetRegularKey wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + SetRegularKey + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return SetRegularKey{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/SignerListSet.h b/include/xrpl/protocol_autogen/transactions/SignerListSet.h new file mode 100644 index 0000000000..ec5330a9cb --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/SignerListSet.h @@ -0,0 +1,164 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class SignerListSetBuilder; + +/** + * @brief Transaction: SignerListSet + * + * Type: ttSIGNER_LIST_SET (12) + * Delegable: Delegation::notDelegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use SignerListSetBuilder to construct new transactions. + */ +class SignerListSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttSIGNER_LIST_SET; + + /** + * @brief Construct a SignerListSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit SignerListSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for SignerListSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfSignerQuorum (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSignerQuorum() const + { + return this->tx_->at(sfSignerQuorum); + } + /** + * @brief Get sfSignerEntries (soeOPTIONAL) + * @note This is an untyped field. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getSignerEntries() const + { + if (this->tx_->isFieldPresent(sfSignerEntries)) + return this->tx_->getFieldArray(sfSignerEntries); + return std::nullopt; + } + + /** + * @brief Check if sfSignerEntries is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSignerEntries() const + { + return this->tx_->isFieldPresent(sfSignerEntries); + } +}; + +/** + * @brief Builder for SignerListSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class SignerListSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new SignerListSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param signerQuorum The sfSignerQuorum field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + SignerListSetBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& signerQuorum, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttSIGNER_LIST_SET, account, sequence, fee) + { + setSignerQuorum(signerQuorum); + } + + /** + * @brief Construct a SignerListSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + SignerListSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttSIGNER_LIST_SET) + { + throw std::runtime_error("Invalid transaction type for SignerListSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfSignerQuorum (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + SignerListSetBuilder& + setSignerQuorum(std::decay_t const& value) + { + object_[sfSignerQuorum] = value; + return *this; + } + + /** + * @brief Set sfSignerEntries (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SignerListSetBuilder& + setSignerEntries(STArray const& value) + { + object_.setFieldArray(sfSignerEntries, value); + return *this; + } + + /** + * @brief Build and return the SignerListSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + SignerListSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return SignerListSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/TicketCreate.h b/include/xrpl/protocol_autogen/transactions/TicketCreate.h new file mode 100644 index 0000000000..842ae93111 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/TicketCreate.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class TicketCreateBuilder; + +/** + * @brief Transaction: TicketCreate + * + * Type: ttTICKET_CREATE (10) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use TicketCreateBuilder to construct new transactions. + */ +class TicketCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttTICKET_CREATE; + + /** + * @brief Construct a TicketCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit TicketCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for TicketCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfTicketCount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getTicketCount() const + { + return this->tx_->at(sfTicketCount); + } +}; + +/** + * @brief Builder for TicketCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class TicketCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new TicketCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param ticketCount The sfTicketCount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + TicketCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& ticketCount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttTICKET_CREATE, account, sequence, fee) + { + setTicketCount(ticketCount); + } + + /** + * @brief Construct a TicketCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + TicketCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttTICKET_CREATE) + { + throw std::runtime_error("Invalid transaction type for TicketCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfTicketCount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + TicketCreateBuilder& + setTicketCount(std::decay_t const& value) + { + object_[sfTicketCount] = value; + return *this; + } + + /** + * @brief Build and return the TicketCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + TicketCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return TicketCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/TrustSet.h b/include/xrpl/protocol_autogen/transactions/TrustSet.h new file mode 100644 index 0000000000..5623311b92 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/TrustSet.h @@ -0,0 +1,216 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class TrustSetBuilder; + +/** + * @brief Transaction: TrustSet + * + * Type: ttTRUST_SET (20) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use TrustSetBuilder to construct new transactions. + */ +class TrustSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttTRUST_SET; + + /** + * @brief Construct a TrustSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit TrustSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for TrustSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLimitAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLimitAmount() const + { + if (hasLimitAmount()) + { + return this->tx_->at(sfLimitAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfLimitAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLimitAmount() const + { + return this->tx_->isFieldPresent(sfLimitAmount); + } + + /** + * @brief Get sfQualityIn (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getQualityIn() const + { + if (hasQualityIn()) + { + return this->tx_->at(sfQualityIn); + } + return std::nullopt; + } + + /** + * @brief Check if sfQualityIn is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasQualityIn() const + { + return this->tx_->isFieldPresent(sfQualityIn); + } + + /** + * @brief Get sfQualityOut (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getQualityOut() const + { + if (hasQualityOut()) + { + return this->tx_->at(sfQualityOut); + } + return std::nullopt; + } + + /** + * @brief Check if sfQualityOut is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasQualityOut() const + { + return this->tx_->isFieldPresent(sfQualityOut); + } +}; + +/** + * @brief Builder for TrustSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class TrustSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new TrustSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + TrustSetBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttTRUST_SET, account, sequence, fee) + { + } + + /** + * @brief Construct a TrustSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + TrustSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttTRUST_SET) + { + throw std::runtime_error("Invalid transaction type for TrustSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLimitAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + TrustSetBuilder& + setLimitAmount(std::decay_t const& value) + { + object_[sfLimitAmount] = value; + return *this; + } + + /** + * @brief Set sfQualityIn (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + TrustSetBuilder& + setQualityIn(std::decay_t const& value) + { + object_[sfQualityIn] = value; + return *this; + } + + /** + * @brief Set sfQualityOut (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + TrustSetBuilder& + setQualityOut(std::decay_t const& value) + { + object_[sfQualityOut] = value; + return *this; + } + + /** + * @brief Build and return the TrustSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + TrustSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return TrustSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/UNLModify.h b/include/xrpl/protocol_autogen/transactions/UNLModify.h new file mode 100644 index 0000000000..b5e64f19a0 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/UNLModify.h @@ -0,0 +1,177 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class UNLModifyBuilder; + +/** + * @brief Transaction: UNLModify + * + * Type: ttUNL_MODIFY (102) + * Delegable: Delegation::notDelegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use UNLModifyBuilder to construct new transactions. + */ +class UNLModify : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttUNL_MODIFY; + + /** + * @brief Construct a UNLModify transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit UNLModify(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for UNLModify"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfUNLModifyDisabling (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT8::type::value_type + getUNLModifyDisabling() const + { + return this->tx_->at(sfUNLModifyDisabling); + } + + /** + * @brief Get sfLedgerSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getLedgerSequence() const + { + return this->tx_->at(sfLedgerSequence); + } + + /** + * @brief Get sfUNLModifyValidator (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getUNLModifyValidator() const + { + return this->tx_->at(sfUNLModifyValidator); + } +}; + +/** + * @brief Builder for UNLModify transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class UNLModifyBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new UNLModifyBuilder with required fields. + * @param account The account initiating the transaction. + * @param uNLModifyDisabling The sfUNLModifyDisabling field value. + * @param ledgerSequence The sfLedgerSequence field value. + * @param uNLModifyValidator The sfUNLModifyValidator field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + UNLModifyBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& uNLModifyDisabling, std::decay_t const& ledgerSequence, std::decay_t const& uNLModifyValidator, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttUNL_MODIFY, account, sequence, fee) + { + setUNLModifyDisabling(uNLModifyDisabling); + setLedgerSequence(ledgerSequence); + setUNLModifyValidator(uNLModifyValidator); + } + + /** + * @brief Construct a UNLModifyBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + UNLModifyBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttUNL_MODIFY) + { + throw std::runtime_error("Invalid transaction type for UNLModifyBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfUNLModifyDisabling (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + UNLModifyBuilder& + setUNLModifyDisabling(std::decay_t const& value) + { + object_[sfUNLModifyDisabling] = value; + return *this; + } + + /** + * @brief Set sfLedgerSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + UNLModifyBuilder& + setLedgerSequence(std::decay_t const& value) + { + object_[sfLedgerSequence] = value; + return *this; + } + + /** + * @brief Set sfUNLModifyValidator (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + UNLModifyBuilder& + setUNLModifyValidator(std::decay_t const& value) + { + object_[sfUNLModifyValidator] = value; + return *this; + } + + /** + * @brief Build and return the UNLModify wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + UNLModify + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return UNLModify{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/VaultClawback.h b/include/xrpl/protocol_autogen/transactions/VaultClawback.h new file mode 100644 index 0000000000..82b2b32e8d --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/VaultClawback.h @@ -0,0 +1,192 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class VaultClawbackBuilder; + +/** + * @brief Transaction: VaultClawback + * + * Type: ttVAULT_CLAWBACK (70) + * Delegable: Delegation::notDelegable + * Amendment: featureSingleAssetVault + * Privileges: mayDeleteMPT | mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use VaultClawbackBuilder to construct new transactions. + */ +class VaultClawback : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttVAULT_CLAWBACK; + + /** + * @brief Construct a VaultClawback transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit VaultClawback(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for VaultClawback"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfVaultID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getVaultID() const + { + return this->tx_->at(sfVaultID); + } + + /** + * @brief Get sfHolder (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getHolder() const + { + return this->tx_->at(sfHolder); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } +}; + +/** + * @brief Builder for VaultClawback transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class VaultClawbackBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new VaultClawbackBuilder with required fields. + * @param account The account initiating the transaction. + * @param vaultID The sfVaultID field value. + * @param holder The sfHolder field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + VaultClawbackBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& vaultID, std::decay_t const& holder, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttVAULT_CLAWBACK, account, sequence, fee) + { + setVaultID(vaultID); + setHolder(holder); + } + + /** + * @brief Construct a VaultClawbackBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + VaultClawbackBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttVAULT_CLAWBACK) + { + throw std::runtime_error("Invalid transaction type for VaultClawbackBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfVaultID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultClawbackBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Set sfHolder (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultClawbackBuilder& + setHolder(std::decay_t const& value) + { + object_[sfHolder] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + VaultClawbackBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Build and return the VaultClawback wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + VaultClawback + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return VaultClawback{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/VaultCreate.h b/include/xrpl/protocol_autogen/transactions/VaultCreate.h new file mode 100644 index 0000000000..03d4d4b88d --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/VaultCreate.h @@ -0,0 +1,353 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class VaultCreateBuilder; + +/** + * @brief Transaction: VaultCreate + * + * Type: ttVAULT_CREATE (65) + * Delegable: Delegation::notDelegable + * Amendment: featureSingleAssetVault + * Privileges: createPseudoAcct | createMPTIssuance | mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use VaultCreateBuilder to construct new transactions. + */ +class VaultCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttVAULT_CREATE; + + /** + * @brief Construct a VaultCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit VaultCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for VaultCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAsset (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->tx_->at(sfAsset); + } + + /** + * @brief Get sfAssetsMaximum (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetsMaximum() const + { + if (hasAssetsMaximum()) + { + return this->tx_->at(sfAssetsMaximum); + } + return std::nullopt; + } + + /** + * @brief Check if sfAssetsMaximum is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetsMaximum() const + { + return this->tx_->isFieldPresent(sfAssetsMaximum); + } + + /** + * @brief Get sfMPTokenMetadata (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMPTokenMetadata() const + { + if (hasMPTokenMetadata()) + { + return this->tx_->at(sfMPTokenMetadata); + } + return std::nullopt; + } + + /** + * @brief Check if sfMPTokenMetadata is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMPTokenMetadata() const + { + return this->tx_->isFieldPresent(sfMPTokenMetadata); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + { + return this->tx_->at(sfDomainID); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->tx_->isFieldPresent(sfDomainID); + } + + /** + * @brief Get sfWithdrawalPolicy (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getWithdrawalPolicy() const + { + if (hasWithdrawalPolicy()) + { + return this->tx_->at(sfWithdrawalPolicy); + } + return std::nullopt; + } + + /** + * @brief Check if sfWithdrawalPolicy is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasWithdrawalPolicy() const + { + return this->tx_->isFieldPresent(sfWithdrawalPolicy); + } + + /** + * @brief Get sfData (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getData() const + { + if (hasData()) + { + return this->tx_->at(sfData); + } + return std::nullopt; + } + + /** + * @brief Check if sfData is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasData() const + { + return this->tx_->isFieldPresent(sfData); + } + + /** + * @brief Get sfScale (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getScale() const + { + if (hasScale()) + { + return this->tx_->at(sfScale); + } + return std::nullopt; + } + + /** + * @brief Check if sfScale is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasScale() const + { + return this->tx_->isFieldPresent(sfScale); + } +}; + +/** + * @brief Builder for VaultCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class VaultCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new VaultCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param asset The sfAsset field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + VaultCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& asset, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttVAULT_CREATE, account, sequence, fee) + { + setAsset(asset); + } + + /** + * @brief Construct a VaultCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + VaultCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttVAULT_CREATE) + { + throw std::runtime_error("Invalid transaction type for VaultCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAsset (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + VaultCreateBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAssetsMaximum (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultCreateBuilder& + setAssetsMaximum(std::decay_t const& value) + { + object_[sfAssetsMaximum] = value; + return *this; + } + + /** + * @brief Set sfMPTokenMetadata (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultCreateBuilder& + setMPTokenMetadata(std::decay_t const& value) + { + object_[sfMPTokenMetadata] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultCreateBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Set sfWithdrawalPolicy (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultCreateBuilder& + setWithdrawalPolicy(std::decay_t const& value) + { + object_[sfWithdrawalPolicy] = value; + return *this; + } + + /** + * @brief Set sfData (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultCreateBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Set sfScale (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultCreateBuilder& + setScale(std::decay_t const& value) + { + object_[sfScale] = value; + return *this; + } + + /** + * @brief Build and return the VaultCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + VaultCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return VaultCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/VaultDelete.h b/include/xrpl/protocol_autogen/transactions/VaultDelete.h new file mode 100644 index 0000000000..89a4ef2a2f --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/VaultDelete.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class VaultDeleteBuilder; + +/** + * @brief Transaction: VaultDelete + * + * Type: ttVAULT_DELETE (67) + * Delegable: Delegation::notDelegable + * Amendment: featureSingleAssetVault + * Privileges: mustDeleteAcct | destroyMPTIssuance | mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use VaultDeleteBuilder to construct new transactions. + */ +class VaultDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttVAULT_DELETE; + + /** + * @brief Construct a VaultDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit VaultDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for VaultDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfVaultID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getVaultID() const + { + return this->tx_->at(sfVaultID); + } +}; + +/** + * @brief Builder for VaultDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class VaultDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new VaultDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param vaultID The sfVaultID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + VaultDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& vaultID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttVAULT_DELETE, account, sequence, fee) + { + setVaultID(vaultID); + } + + /** + * @brief Construct a VaultDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + VaultDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttVAULT_DELETE) + { + throw std::runtime_error("Invalid transaction type for VaultDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfVaultID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultDeleteBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Build and return the VaultDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + VaultDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return VaultDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/VaultDeposit.h b/include/xrpl/protocol_autogen/transactions/VaultDeposit.h new file mode 100644 index 0000000000..4127638e57 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/VaultDeposit.h @@ -0,0 +1,155 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class VaultDepositBuilder; + +/** + * @brief Transaction: VaultDeposit + * + * Type: ttVAULT_DEPOSIT (68) + * Delegable: Delegation::notDelegable + * Amendment: featureSingleAssetVault + * Privileges: mayAuthorizeMPT | mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use VaultDepositBuilder to construct new transactions. + */ +class VaultDeposit : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttVAULT_DEPOSIT; + + /** + * @brief Construct a VaultDeposit transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit VaultDeposit(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for VaultDeposit"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfVaultID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getVaultID() const + { + return this->tx_->at(sfVaultID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } +}; + +/** + * @brief Builder for VaultDeposit transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class VaultDepositBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new VaultDepositBuilder with required fields. + * @param account The account initiating the transaction. + * @param vaultID The sfVaultID field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + VaultDepositBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& vaultID, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttVAULT_DEPOSIT, account, sequence, fee) + { + setVaultID(vaultID); + setAmount(amount); + } + + /** + * @brief Construct a VaultDepositBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + VaultDepositBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttVAULT_DEPOSIT) + { + throw std::runtime_error("Invalid transaction type for VaultDepositBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfVaultID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultDepositBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + VaultDepositBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Build and return the VaultDeposit wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + VaultDeposit + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return VaultDeposit{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/VaultSet.h b/include/xrpl/protocol_autogen/transactions/VaultSet.h new file mode 100644 index 0000000000..372697b5f7 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/VaultSet.h @@ -0,0 +1,240 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class VaultSetBuilder; + +/** + * @brief Transaction: VaultSet + * + * Type: ttVAULT_SET (66) + * Delegable: Delegation::notDelegable + * Amendment: featureSingleAssetVault + * Privileges: mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use VaultSetBuilder to construct new transactions. + */ +class VaultSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttVAULT_SET; + + /** + * @brief Construct a VaultSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit VaultSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for VaultSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfVaultID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getVaultID() const + { + return this->tx_->at(sfVaultID); + } + + /** + * @brief Get sfAssetsMaximum (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetsMaximum() const + { + if (hasAssetsMaximum()) + { + return this->tx_->at(sfAssetsMaximum); + } + return std::nullopt; + } + + /** + * @brief Check if sfAssetsMaximum is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetsMaximum() const + { + return this->tx_->isFieldPresent(sfAssetsMaximum); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + { + return this->tx_->at(sfDomainID); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->tx_->isFieldPresent(sfDomainID); + } + + /** + * @brief Get sfData (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getData() const + { + if (hasData()) + { + return this->tx_->at(sfData); + } + return std::nullopt; + } + + /** + * @brief Check if sfData is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasData() const + { + return this->tx_->isFieldPresent(sfData); + } +}; + +/** + * @brief Builder for VaultSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class VaultSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new VaultSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param vaultID The sfVaultID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + VaultSetBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& vaultID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttVAULT_SET, account, sequence, fee) + { + setVaultID(vaultID); + } + + /** + * @brief Construct a VaultSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + VaultSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttVAULT_SET) + { + throw std::runtime_error("Invalid transaction type for VaultSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfVaultID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultSetBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Set sfAssetsMaximum (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultSetBuilder& + setAssetsMaximum(std::decay_t const& value) + { + object_[sfAssetsMaximum] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultSetBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Set sfData (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultSetBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Build and return the VaultSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + VaultSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return VaultSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/VaultWithdraw.h b/include/xrpl/protocol_autogen/transactions/VaultWithdraw.h new file mode 100644 index 0000000000..8b2a6a9a3c --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/VaultWithdraw.h @@ -0,0 +1,229 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class VaultWithdrawBuilder; + +/** + * @brief Transaction: VaultWithdraw + * + * Type: ttVAULT_WITHDRAW (69) + * Delegable: Delegation::notDelegable + * Amendment: featureSingleAssetVault + * Privileges: mayDeleteMPT | mayAuthorizeMPT | mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use VaultWithdrawBuilder to construct new transactions. + */ +class VaultWithdraw : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttVAULT_WITHDRAW; + + /** + * @brief Construct a VaultWithdraw transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit VaultWithdraw(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for VaultWithdraw"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfVaultID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getVaultID() const + { + return this->tx_->at(sfVaultID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfDestination (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestination() const + { + if (hasDestination()) + { + return this->tx_->at(sfDestination); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestination is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestination() const + { + return this->tx_->isFieldPresent(sfDestination); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationTag() const + { + if (hasDestinationTag()) + { + return this->tx_->at(sfDestinationTag); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestinationTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestinationTag() const + { + return this->tx_->isFieldPresent(sfDestinationTag); + } +}; + +/** + * @brief Builder for VaultWithdraw transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class VaultWithdrawBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new VaultWithdrawBuilder with required fields. + * @param account The account initiating the transaction. + * @param vaultID The sfVaultID field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + VaultWithdrawBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& vaultID, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttVAULT_WITHDRAW, account, sequence, fee) + { + setVaultID(vaultID); + setAmount(amount); + } + + /** + * @brief Construct a VaultWithdrawBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + VaultWithdrawBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttVAULT_WITHDRAW) + { + throw std::runtime_error("Invalid transaction type for VaultWithdrawBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfVaultID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultWithdrawBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + VaultWithdrawBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultWithdrawBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultWithdrawBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Build and return the VaultWithdraw wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + VaultWithdraw + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return VaultWithdraw{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainAccountCreateCommit.h b/include/xrpl/protocol_autogen/transactions/XChainAccountCreateCommit.h new file mode 100644 index 0000000000..aebfbea5d3 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainAccountCreateCommit.h @@ -0,0 +1,201 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainAccountCreateCommitBuilder; + +/** + * @brief Transaction: XChainAccountCreateCommit + * + * Type: ttXCHAIN_ACCOUNT_CREATE_COMMIT (44) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainAccountCreateCommitBuilder to construct new transactions. + */ +class XChainAccountCreateCommit : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_ACCOUNT_CREATE_COMMIT; + + /** + * @brief Construct a XChainAccountCreateCommit transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainAccountCreateCommit(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainAccountCreateCommit"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfSignatureReward (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSignatureReward() const + { + return this->tx_->at(sfSignatureReward); + } +}; + +/** + * @brief Builder for XChainAccountCreateCommit transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainAccountCreateCommitBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainAccountCreateCommitBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param destination The sfDestination field value. + * @param amount The sfAmount field value. + * @param signatureReward The sfSignatureReward field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainAccountCreateCommitBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::decay_t const& destination, std::decay_t const& amount, std::decay_t const& signatureReward, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_ACCOUNT_CREATE_COMMIT, account, sequence, fee) + { + setXChainBridge(xChainBridge); + setDestination(destination); + setAmount(amount); + setSignatureReward(signatureReward); + } + + /** + * @brief Construct a XChainAccountCreateCommitBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainAccountCreateCommitBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_ACCOUNT_CREATE_COMMIT) + { + throw std::runtime_error("Invalid transaction type for XChainAccountCreateCommitBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAccountCreateCommitBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAccountCreateCommitBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAccountCreateCommitBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfSignatureReward (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAccountCreateCommitBuilder& + setSignatureReward(std::decay_t const& value) + { + object_[sfSignatureReward] = value; + return *this; + } + + /** + * @brief Build and return the XChainAccountCreateCommit wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainAccountCreateCommit + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainAccountCreateCommit{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestation.h b/include/xrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestation.h new file mode 100644 index 0000000000..08dc646470 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestation.h @@ -0,0 +1,369 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainAddAccountCreateAttestationBuilder; + +/** + * @brief Transaction: XChainAddAccountCreateAttestation + * + * Type: ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION (46) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: createAcct + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainAddAccountCreateAttestationBuilder to construct new transactions. + */ +class XChainAddAccountCreateAttestation : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION; + + /** + * @brief Construct a XChainAddAccountCreateAttestation transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainAddAccountCreateAttestation(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainAddAccountCreateAttestation"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfAttestationSignerAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAttestationSignerAccount() const + { + return this->tx_->at(sfAttestationSignerAccount); + } + + /** + * @brief Get sfPublicKey (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getPublicKey() const + { + return this->tx_->at(sfPublicKey); + } + + /** + * @brief Get sfSignature (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getSignature() const + { + return this->tx_->at(sfSignature); + } + + /** + * @brief Get sfOtherChainSource (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOtherChainSource() const + { + return this->tx_->at(sfOtherChainSource); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfAttestationRewardAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAttestationRewardAccount() const + { + return this->tx_->at(sfAttestationRewardAccount); + } + + /** + * @brief Get sfWasLockingChainSend (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT8::type::value_type + getWasLockingChainSend() const + { + return this->tx_->at(sfWasLockingChainSend); + } + + /** + * @brief Get sfXChainAccountCreateCount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainAccountCreateCount() const + { + return this->tx_->at(sfXChainAccountCreateCount); + } + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfSignatureReward (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSignatureReward() const + { + return this->tx_->at(sfSignatureReward); + } +}; + +/** + * @brief Builder for XChainAddAccountCreateAttestation transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainAddAccountCreateAttestationBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainAddAccountCreateAttestationBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param attestationSignerAccount The sfAttestationSignerAccount field value. + * @param publicKey The sfPublicKey field value. + * @param signature The sfSignature field value. + * @param otherChainSource The sfOtherChainSource field value. + * @param amount The sfAmount field value. + * @param attestationRewardAccount The sfAttestationRewardAccount field value. + * @param wasLockingChainSend The sfWasLockingChainSend field value. + * @param xChainAccountCreateCount The sfXChainAccountCreateCount field value. + * @param destination The sfDestination field value. + * @param signatureReward The sfSignatureReward field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainAddAccountCreateAttestationBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::decay_t const& attestationSignerAccount, std::decay_t const& publicKey, std::decay_t const& signature, std::decay_t const& otherChainSource, std::decay_t const& amount, std::decay_t const& attestationRewardAccount, std::decay_t const& wasLockingChainSend, std::decay_t const& xChainAccountCreateCount, std::decay_t const& destination, std::decay_t const& signatureReward, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, account, sequence, fee) + { + setXChainBridge(xChainBridge); + setAttestationSignerAccount(attestationSignerAccount); + setPublicKey(publicKey); + setSignature(signature); + setOtherChainSource(otherChainSource); + setAmount(amount); + setAttestationRewardAccount(attestationRewardAccount); + setWasLockingChainSend(wasLockingChainSend); + setXChainAccountCreateCount(xChainAccountCreateCount); + setDestination(destination); + setSignatureReward(signatureReward); + } + + /** + * @brief Construct a XChainAddAccountCreateAttestationBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainAddAccountCreateAttestationBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION) + { + throw std::runtime_error("Invalid transaction type for XChainAddAccountCreateAttestationBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfAttestationSignerAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setAttestationSignerAccount(std::decay_t const& value) + { + object_[sfAttestationSignerAccount] = value; + return *this; + } + + /** + * @brief Set sfPublicKey (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setPublicKey(std::decay_t const& value) + { + object_[sfPublicKey] = value; + return *this; + } + + /** + * @brief Set sfSignature (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setSignature(std::decay_t const& value) + { + object_[sfSignature] = value; + return *this; + } + + /** + * @brief Set sfOtherChainSource (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setOtherChainSource(std::decay_t const& value) + { + object_[sfOtherChainSource] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfAttestationRewardAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setAttestationRewardAccount(std::decay_t const& value) + { + object_[sfAttestationRewardAccount] = value; + return *this; + } + + /** + * @brief Set sfWasLockingChainSend (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setWasLockingChainSend(std::decay_t const& value) + { + object_[sfWasLockingChainSend] = value; + return *this; + } + + /** + * @brief Set sfXChainAccountCreateCount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setXChainAccountCreateCount(std::decay_t const& value) + { + object_[sfXChainAccountCreateCount] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfSignatureReward (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setSignatureReward(std::decay_t const& value) + { + object_[sfSignatureReward] = value; + return *this; + } + + /** + * @brief Build and return the XChainAddAccountCreateAttestation wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainAddAccountCreateAttestation + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainAddAccountCreateAttestation{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainAddClaimAttestation.h b/include/xrpl/protocol_autogen/transactions/XChainAddClaimAttestation.h new file mode 100644 index 0000000000..100fc855aa --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainAddClaimAttestation.h @@ -0,0 +1,358 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainAddClaimAttestationBuilder; + +/** + * @brief Transaction: XChainAddClaimAttestation + * + * Type: ttXCHAIN_ADD_CLAIM_ATTESTATION (45) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: createAcct + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainAddClaimAttestationBuilder to construct new transactions. + */ +class XChainAddClaimAttestation : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_ADD_CLAIM_ATTESTATION; + + /** + * @brief Construct a XChainAddClaimAttestation transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainAddClaimAttestation(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainAddClaimAttestation"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfAttestationSignerAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAttestationSignerAccount() const + { + return this->tx_->at(sfAttestationSignerAccount); + } + + /** + * @brief Get sfPublicKey (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getPublicKey() const + { + return this->tx_->at(sfPublicKey); + } + + /** + * @brief Get sfSignature (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getSignature() const + { + return this->tx_->at(sfSignature); + } + + /** + * @brief Get sfOtherChainSource (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOtherChainSource() const + { + return this->tx_->at(sfOtherChainSource); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfAttestationRewardAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAttestationRewardAccount() const + { + return this->tx_->at(sfAttestationRewardAccount); + } + + /** + * @brief Get sfWasLockingChainSend (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT8::type::value_type + getWasLockingChainSend() const + { + return this->tx_->at(sfWasLockingChainSend); + } + + /** + * @brief Get sfXChainClaimID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainClaimID() const + { + return this->tx_->at(sfXChainClaimID); + } + + /** + * @brief Get sfDestination (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestination() const + { + if (hasDestination()) + { + return this->tx_->at(sfDestination); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestination is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestination() const + { + return this->tx_->isFieldPresent(sfDestination); + } +}; + +/** + * @brief Builder for XChainAddClaimAttestation transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainAddClaimAttestationBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainAddClaimAttestationBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param attestationSignerAccount The sfAttestationSignerAccount field value. + * @param publicKey The sfPublicKey field value. + * @param signature The sfSignature field value. + * @param otherChainSource The sfOtherChainSource field value. + * @param amount The sfAmount field value. + * @param attestationRewardAccount The sfAttestationRewardAccount field value. + * @param wasLockingChainSend The sfWasLockingChainSend field value. + * @param xChainClaimID The sfXChainClaimID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainAddClaimAttestationBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::decay_t const& attestationSignerAccount, std::decay_t const& publicKey, std::decay_t const& signature, std::decay_t const& otherChainSource, std::decay_t const& amount, std::decay_t const& attestationRewardAccount, std::decay_t const& wasLockingChainSend, std::decay_t const& xChainClaimID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_ADD_CLAIM_ATTESTATION, account, sequence, fee) + { + setXChainBridge(xChainBridge); + setAttestationSignerAccount(attestationSignerAccount); + setPublicKey(publicKey); + setSignature(signature); + setOtherChainSource(otherChainSource); + setAmount(amount); + setAttestationRewardAccount(attestationRewardAccount); + setWasLockingChainSend(wasLockingChainSend); + setXChainClaimID(xChainClaimID); + } + + /** + * @brief Construct a XChainAddClaimAttestationBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainAddClaimAttestationBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_ADD_CLAIM_ATTESTATION) + { + throw std::runtime_error("Invalid transaction type for XChainAddClaimAttestationBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfAttestationSignerAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setAttestationSignerAccount(std::decay_t const& value) + { + object_[sfAttestationSignerAccount] = value; + return *this; + } + + /** + * @brief Set sfPublicKey (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setPublicKey(std::decay_t const& value) + { + object_[sfPublicKey] = value; + return *this; + } + + /** + * @brief Set sfSignature (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setSignature(std::decay_t const& value) + { + object_[sfSignature] = value; + return *this; + } + + /** + * @brief Set sfOtherChainSource (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setOtherChainSource(std::decay_t const& value) + { + object_[sfOtherChainSource] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfAttestationRewardAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setAttestationRewardAccount(std::decay_t const& value) + { + object_[sfAttestationRewardAccount] = value; + return *this; + } + + /** + * @brief Set sfWasLockingChainSend (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setWasLockingChainSend(std::decay_t const& value) + { + object_[sfWasLockingChainSend] = value; + return *this; + } + + /** + * @brief Set sfXChainClaimID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setXChainClaimID(std::decay_t const& value) + { + object_[sfXChainClaimID] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Build and return the XChainAddClaimAttestation wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainAddClaimAttestation + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainAddClaimAttestation{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainClaim.h b/include/xrpl/protocol_autogen/transactions/XChainClaim.h new file mode 100644 index 0000000000..6b1b748677 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainClaim.h @@ -0,0 +1,238 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainClaimBuilder; + +/** + * @brief Transaction: XChainClaim + * + * Type: ttXCHAIN_CLAIM (43) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainClaimBuilder to construct new transactions. + */ +class XChainClaim : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_CLAIM; + + /** + * @brief Construct a XChainClaim transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainClaim(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainClaim"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfXChainClaimID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainClaimID() const + { + return this->tx_->at(sfXChainClaimID); + } + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationTag() const + { + if (hasDestinationTag()) + { + return this->tx_->at(sfDestinationTag); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestinationTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestinationTag() const + { + return this->tx_->isFieldPresent(sfDestinationTag); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } +}; + +/** + * @brief Builder for XChainClaim transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainClaimBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainClaimBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param xChainClaimID The sfXChainClaimID field value. + * @param destination The sfDestination field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainClaimBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::decay_t const& xChainClaimID, std::decay_t const& destination, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_CLAIM, account, sequence, fee) + { + setXChainBridge(xChainBridge); + setXChainClaimID(xChainClaimID); + setDestination(destination); + setAmount(amount); + } + + /** + * @brief Construct a XChainClaimBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainClaimBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_CLAIM) + { + throw std::runtime_error("Invalid transaction type for XChainClaimBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainClaimBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfXChainClaimID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainClaimBuilder& + setXChainClaimID(std::decay_t const& value) + { + object_[sfXChainClaimID] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainClaimBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + XChainClaimBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainClaimBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Build and return the XChainClaim wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainClaim + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainClaim{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainCommit.h b/include/xrpl/protocol_autogen/transactions/XChainCommit.h new file mode 100644 index 0000000000..879c76f180 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainCommit.h @@ -0,0 +1,214 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainCommitBuilder; + +/** + * @brief Transaction: XChainCommit + * + * Type: ttXCHAIN_COMMIT (42) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainCommitBuilder to construct new transactions. + */ +class XChainCommit : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_COMMIT; + + /** + * @brief Construct a XChainCommit transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainCommit(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainCommit"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfXChainClaimID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainClaimID() const + { + return this->tx_->at(sfXChainClaimID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfOtherChainDestination (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOtherChainDestination() const + { + if (hasOtherChainDestination()) + { + return this->tx_->at(sfOtherChainDestination); + } + return std::nullopt; + } + + /** + * @brief Check if sfOtherChainDestination is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOtherChainDestination() const + { + return this->tx_->isFieldPresent(sfOtherChainDestination); + } +}; + +/** + * @brief Builder for XChainCommit transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainCommitBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainCommitBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param xChainClaimID The sfXChainClaimID field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainCommitBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::decay_t const& xChainClaimID, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_COMMIT, account, sequence, fee) + { + setXChainBridge(xChainBridge); + setXChainClaimID(xChainClaimID); + setAmount(amount); + } + + /** + * @brief Construct a XChainCommitBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainCommitBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_COMMIT) + { + throw std::runtime_error("Invalid transaction type for XChainCommitBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCommitBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfXChainClaimID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCommitBuilder& + setXChainClaimID(std::decay_t const& value) + { + object_[sfXChainClaimID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCommitBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfOtherChainDestination (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + XChainCommitBuilder& + setOtherChainDestination(std::decay_t const& value) + { + object_[sfOtherChainDestination] = value; + return *this; + } + + /** + * @brief Build and return the XChainCommit wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainCommit + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainCommit{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainCreateBridge.h b/include/xrpl/protocol_autogen/transactions/XChainCreateBridge.h new file mode 100644 index 0000000000..6ec22dd9fc --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainCreateBridge.h @@ -0,0 +1,190 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainCreateBridgeBuilder; + +/** + * @brief Transaction: XChainCreateBridge + * + * Type: ttXCHAIN_CREATE_BRIDGE (48) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainCreateBridgeBuilder to construct new transactions. + */ +class XChainCreateBridge : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_CREATE_BRIDGE; + + /** + * @brief Construct a XChainCreateBridge transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainCreateBridge(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainCreateBridge"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfSignatureReward (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSignatureReward() const + { + return this->tx_->at(sfSignatureReward); + } + + /** + * @brief Get sfMinAccountCreateAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMinAccountCreateAmount() const + { + if (hasMinAccountCreateAmount()) + { + return this->tx_->at(sfMinAccountCreateAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfMinAccountCreateAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMinAccountCreateAmount() const + { + return this->tx_->isFieldPresent(sfMinAccountCreateAmount); + } +}; + +/** + * @brief Builder for XChainCreateBridge transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainCreateBridgeBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainCreateBridgeBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param signatureReward The sfSignatureReward field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainCreateBridgeBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::decay_t const& signatureReward, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_CREATE_BRIDGE, account, sequence, fee) + { + setXChainBridge(xChainBridge); + setSignatureReward(signatureReward); + } + + /** + * @brief Construct a XChainCreateBridgeBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainCreateBridgeBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_CREATE_BRIDGE) + { + throw std::runtime_error("Invalid transaction type for XChainCreateBridgeBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCreateBridgeBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfSignatureReward (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCreateBridgeBuilder& + setSignatureReward(std::decay_t const& value) + { + object_[sfSignatureReward] = value; + return *this; + } + + /** + * @brief Set sfMinAccountCreateAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + XChainCreateBridgeBuilder& + setMinAccountCreateAmount(std::decay_t const& value) + { + object_[sfMinAccountCreateAmount] = value; + return *this; + } + + /** + * @brief Build and return the XChainCreateBridge wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainCreateBridge + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainCreateBridge{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainCreateClaimID.h b/include/xrpl/protocol_autogen/transactions/XChainCreateClaimID.h new file mode 100644 index 0000000000..ab76ee0e77 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainCreateClaimID.h @@ -0,0 +1,177 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainCreateClaimIDBuilder; + +/** + * @brief Transaction: XChainCreateClaimID + * + * Type: ttXCHAIN_CREATE_CLAIM_ID (41) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainCreateClaimIDBuilder to construct new transactions. + */ +class XChainCreateClaimID : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_CREATE_CLAIM_ID; + + /** + * @brief Construct a XChainCreateClaimID transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainCreateClaimID(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainCreateClaimID"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfSignatureReward (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSignatureReward() const + { + return this->tx_->at(sfSignatureReward); + } + + /** + * @brief Get sfOtherChainSource (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOtherChainSource() const + { + return this->tx_->at(sfOtherChainSource); + } +}; + +/** + * @brief Builder for XChainCreateClaimID transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainCreateClaimIDBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainCreateClaimIDBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param signatureReward The sfSignatureReward field value. + * @param otherChainSource The sfOtherChainSource field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainCreateClaimIDBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::decay_t const& signatureReward, std::decay_t const& otherChainSource, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_CREATE_CLAIM_ID, account, sequence, fee) + { + setXChainBridge(xChainBridge); + setSignatureReward(signatureReward); + setOtherChainSource(otherChainSource); + } + + /** + * @brief Construct a XChainCreateClaimIDBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainCreateClaimIDBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_CREATE_CLAIM_ID) + { + throw std::runtime_error("Invalid transaction type for XChainCreateClaimIDBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCreateClaimIDBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfSignatureReward (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCreateClaimIDBuilder& + setSignatureReward(std::decay_t const& value) + { + object_[sfSignatureReward] = value; + return *this; + } + + /** + * @brief Set sfOtherChainSource (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCreateClaimIDBuilder& + setOtherChainSource(std::decay_t const& value) + { + object_[sfOtherChainSource] = value; + return *this; + } + + /** + * @brief Build and return the XChainCreateClaimID wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainCreateClaimID + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainCreateClaimID{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainModifyBridge.h b/include/xrpl/protocol_autogen/transactions/XChainModifyBridge.h new file mode 100644 index 0000000000..b57e0fdbdc --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainModifyBridge.h @@ -0,0 +1,203 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainModifyBridgeBuilder; + +/** + * @brief Transaction: XChainModifyBridge + * + * Type: ttXCHAIN_MODIFY_BRIDGE (47) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainModifyBridgeBuilder to construct new transactions. + */ +class XChainModifyBridge : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_MODIFY_BRIDGE; + + /** + * @brief Construct a XChainModifyBridge transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainModifyBridge(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainModifyBridge"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfSignatureReward (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSignatureReward() const + { + if (hasSignatureReward()) + { + return this->tx_->at(sfSignatureReward); + } + return std::nullopt; + } + + /** + * @brief Check if sfSignatureReward is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSignatureReward() const + { + return this->tx_->isFieldPresent(sfSignatureReward); + } + + /** + * @brief Get sfMinAccountCreateAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMinAccountCreateAmount() const + { + if (hasMinAccountCreateAmount()) + { + return this->tx_->at(sfMinAccountCreateAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfMinAccountCreateAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMinAccountCreateAmount() const + { + return this->tx_->isFieldPresent(sfMinAccountCreateAmount); + } +}; + +/** + * @brief Builder for XChainModifyBridge transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainModifyBridgeBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainModifyBridgeBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainModifyBridgeBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_MODIFY_BRIDGE, account, sequence, fee) + { + setXChainBridge(xChainBridge); + } + + /** + * @brief Construct a XChainModifyBridgeBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainModifyBridgeBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_MODIFY_BRIDGE) + { + throw std::runtime_error("Invalid transaction type for XChainModifyBridgeBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainModifyBridgeBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfSignatureReward (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + XChainModifyBridgeBuilder& + setSignatureReward(std::decay_t const& value) + { + object_[sfSignatureReward] = value; + return *this; + } + + /** + * @brief Set sfMinAccountCreateAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + XChainModifyBridgeBuilder& + setMinAccountCreateAmount(std::decay_t const& value) + { + object_[sfMinAccountCreateAmount] = value; + return *this; + } + + /** + * @brief Build and return the XChainModifyBridge wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainModifyBridge + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainModifyBridge{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/rdb/DatabaseCon.h b/include/xrpl/rdb/DatabaseCon.h index d1fb7114e3..7400593214 100644 --- a/include/xrpl/rdb/DatabaseCon.h +++ b/include/xrpl/rdb/DatabaseCon.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -13,7 +14,7 @@ namespace soci { class session; -} +} // namespace soci namespace xrpl { @@ -69,7 +70,7 @@ public: { explicit Setup() = default; - StartUpType startUp = StartUpType::NORMAL; + StartUpType startUp = StartUpType::Normal; bool standAlone = false; boost::filesystem::path dataDir; // Indicates whether or not to return the `globalPragma` @@ -94,7 +95,7 @@ public: struct CheckpointerSetup { JobQueue* jobQueue; - Logs* logs; + std::reference_wrapper registry; }; template @@ -106,9 +107,8 @@ public: beast::Journal journal) // Use temporary files or regular DB files? : DatabaseCon( - setup.standAlone && setup.startUp != StartUpType::LOAD && - setup.startUp != StartUpType::LOAD_FILE && - setup.startUp != StartUpType::REPLAY + setup.standAlone && setup.startUp != StartUpType::Load && + setup.startUp != StartUpType::LoadFile && setup.startUp != StartUpType::Replay ? "" : (setup.dataDir / dbName), setup.commonPragma(), @@ -129,7 +129,7 @@ public: beast::Journal journal) : DatabaseCon(setup, dbName, pragma, initSQL, journal) { - setupCheckpointing(checkpointerSetup.jobQueue, *checkpointerSetup.logs); + setupCheckpointing(checkpointerSetup.jobQueue, checkpointerSetup.registry.get()); } template @@ -154,7 +154,7 @@ public: beast::Journal journal) : DatabaseCon(dataDir, dbName, pragma, initSQL, journal) { - setupCheckpointing(checkpointerSetup.jobQueue, *checkpointerSetup.logs); + setupCheckpointing(checkpointerSetup.jobQueue, checkpointerSetup.registry.get()); } ~DatabaseCon(); @@ -177,7 +177,7 @@ public: private: void - setupCheckpointing(JobQueue*, Logs&); + setupCheckpointing(JobQueue*, ServiceRegistry&); template DatabaseCon( diff --git a/include/xrpl/rdb/RelationalDatabase.h b/include/xrpl/rdb/RelationalDatabase.h index e728e518aa..1c28ddec0a 100644 --- a/include/xrpl/rdb/RelationalDatabase.h +++ b/include/xrpl/rdb/RelationalDatabase.h @@ -49,21 +49,21 @@ public: struct AccountTxOptions { AccountID const& account; - std::uint32_t minLedger; - std::uint32_t maxLedger; - std::uint32_t offset; - std::uint32_t limit; - bool bUnlimited; + /// Ledger sequence range to search. A value of 0 for min or max + /// means unbounded in that direction (no constraint applied). + LedgerRange ledgerRange{}; + std::uint32_t offset = 0; + std::uint32_t limit = 0; + bool bUnlimited{}; }; struct AccountTxPageOptions { AccountID const& account; - std::uint32_t minLedger; - std::uint32_t maxLedger; + LedgerRange ledgerRange{}; std::optional marker; - std::uint32_t limit; - bool bAdmin; + std::uint32_t limit = 0; + bool bAdmin = false; }; using AccountTx = std::pair, std::shared_ptr>; @@ -88,8 +88,8 @@ public: struct AccountTxResult { std::variant transactions; - LedgerRange ledgerRange; - uint32_t limit; + LedgerRange ledgerRange{}; + uint32_t limit = 0; std::optional marker; }; @@ -247,7 +247,7 @@ public: * @return Struct CountMinMax which contains the minimum sequence, * maximum sequence and number of ledgers. */ - virtual struct CountMinMax + virtual CountMinMax getLedgerCountMinMax() = 0; /** @@ -405,10 +405,10 @@ public: * @param id Hash of the transaction. * @param range Range of ledgers to check, if present. * @param ec Default error code value. - * @return Transaction and its metadata if found, otherwise TxSearched::all + * @return Transaction and its metadata if found, otherwise TxSearched::All * if a range is provided and all ledgers from the range are present - * in the database, TxSearched::some if a range is provided and not - * all ledgers are present, TxSearched::unknown if the range is not + * in the database, TxSearched::Some if a range is provided and not + * all ledgers are present, TxSearched::Unknown if the range is not * provided or a deserializing error occurred. In the last case the * error code is returned via the ec parameter, in other cases the * default error code is not changed. @@ -455,9 +455,10 @@ public: closeTransactionDB() = 0; }; -template +template T rangeCheckedCast(C c) + requires(std::is_arithmetic_v && std::is_arithmetic_v && std::convertible_to) { if ((c > std::numeric_limits::max()) || (!std::numeric_limits::is_signed && c < 0) || (std::numeric_limits::is_signed && std::numeric_limits::is_signed && diff --git a/include/xrpl/rdb/SociDB.h b/include/xrpl/rdb/SociDB.h index acfc183076..9c575359c1 100644 --- a/include/xrpl/rdb/SociDB.h +++ b/include/xrpl/rdb/SociDB.h @@ -13,8 +13,8 @@ #pragma clang diagnostic ignored "-Wdeprecated" #endif -#include #include +#include #define SOCI_USE_BOOST #include @@ -25,7 +25,7 @@ namespace sqlite_api { struct sqlite3; -} +} // namespace sqlite_api namespace xrpl { @@ -111,7 +111,7 @@ public: and so must outlive them both. */ std::shared_ptr -makeCheckpointer(std::uintptr_t id, std::weak_ptr, JobQueue&, Logs&); +makeCheckpointer(std::uintptr_t id, std::weak_ptr, JobQueue&, ServiceRegistry&); } // namespace xrpl diff --git a/include/xrpl/resource/Fees.h b/include/xrpl/resource/Fees.h index 0ba083d4fe..ebda0e98db 100644 --- a/include/xrpl/resource/Fees.h +++ b/include/xrpl/resource/Fees.h @@ -5,38 +5,30 @@ namespace xrpl { namespace Resource { -// clang-format off /** Schedule of fees charged for imposing load on the server. */ /** @{ */ -extern Charge const feeMalformedRequest; // A request that we can immediately - // tell is invalid -extern Charge const feeRequestNoReply; // A request that we cannot satisfy -extern Charge const feeInvalidSignature; // An object whose signature we had - // to check and it failed -extern Charge const feeUselessData; // Data we have no use for -extern Charge const feeInvalidData; // Data we have to verify before - // rejecting +extern Charge const feeMalformedRequest; // A request that we can immediately tell is invalid. +extern Charge const feeRequestNoReply; // A request that we cannot satisfy. +extern Charge const feeInvalidSignature; // An object whose signature we had to check that failed. +extern Charge const feeUselessData; // Data we have no use for. +extern Charge const feeInvalidData; // Data we have to verify before rejecting. // RPC loads -extern Charge const feeMalformedRPC; // An RPC request that we can - // immediately tell is invalid. -extern Charge const feeReferenceRPC; // A default "reference" unspecified - // load -extern Charge const feeExceptionRPC; // RPC load that causes an exception -extern Charge const feeMediumBurdenRPC; // A somewhat burdensome RPC load -extern Charge const feeHeavyBurdenRPC; // A very burdensome RPC load +extern Charge const feeMalformedRPC; // An RPC request that we can immediately tell is invalid. +extern Charge const feeReferenceRPC; // A default "reference" unspecified load. +extern Charge const feeExceptionRPC; // RPC load that causes an exception. +extern Charge const feeMediumBurdenRPC; // A somewhat burdensome RPC load. +extern Charge const feeHeavyBurdenRPC; // A very burdensome RPC load. // Peer loads -extern Charge const feeTrivialPeer; // Requires no reply -extern Charge const feeModerateBurdenPeer; // Requires some work -extern Charge const feeHeavyBurdenPeer; // Extensive work +extern Charge const feeTrivialPeer; // Requires no reply. +extern Charge const feeModerateBurdenPeer; // Requires some work. +extern Charge const feeHeavyBurdenPeer; // Extensive work. // Administrative -extern Charge const feeWarning; // The cost of receiving a warning -extern Charge const feeDrop; // The cost of being dropped for - // excess load +extern Charge const feeWarning; // The cost of receiving a warning. +extern Charge const feeDrop; // The cost of being dropped for excess load. /** @} */ -// clang-format on } // namespace Resource } // namespace xrpl diff --git a/include/xrpl/resource/Gossip.h b/include/xrpl/resource/Gossip.h index e581145149..9a70309cc4 100644 --- a/include/xrpl/resource/Gossip.h +++ b/include/xrpl/resource/Gossip.h @@ -17,7 +17,7 @@ struct Gossip { explicit Item() = default; - int balance; + int balance{}; beast::IP::Endpoint address; }; diff --git a/include/xrpl/resource/README.md b/include/xrpl/resource/README.md index e525ce83e3..545d4e9ca0 100644 --- a/include/xrpl/resource/README.md +++ b/include/xrpl/resource/README.md @@ -17,7 +17,7 @@ performed, or simply disconnecting the endpoint. Currently, consumption endpoints include websocket connections used to service clients, and peer connections used to create the peer to peer -overlay network implementing the Ripple protocol. +overlay network implementing the XRPL protocol. The current "balance" of a Consumer represents resource consumption debt or credit. Debt is accrued when bad loads are imposed. Credit is @@ -72,6 +72,6 @@ drop connections to those IP addresses that occur commonly in the gossip. ## Access -In rippled, the Application holds a unique instance of Resource::Manager, +In xrpld, the Application holds a unique instance of Resource::Manager, which may be retrieved by calling the method `Application::getResourceManager()`. diff --git a/include/xrpl/resource/detail/Entry.h b/include/xrpl/resource/detail/Entry.h index ecff8828af..2a6414778c 100644 --- a/include/xrpl/resource/detail/Entry.h +++ b/include/xrpl/resource/detail/Entry.h @@ -62,7 +62,7 @@ struct Entry : public beast::List::Node std::optional publicKey; // Back pointer to the map key (bit of a hack here) - Key const* key; + Key const* key{}; // Number of Consumer references int refcount; diff --git a/include/xrpl/resource/detail/Import.h b/include/xrpl/resource/detail/Import.h index 790fc0596a..bee90afbf0 100644 --- a/include/xrpl/resource/detail/Import.h +++ b/include/xrpl/resource/detail/Import.h @@ -13,7 +13,7 @@ struct Import { explicit Item() = default; - int balance; + int balance{}; Consumer consumer; }; diff --git a/include/xrpl/resource/detail/Logic.h b/include/xrpl/resource/detail/Logic.h index 9a7959f481..66c47bc538 100644 --- a/include/xrpl/resource/detail/Logic.h +++ b/include/xrpl/resource/detail/Logic.h @@ -93,7 +93,7 @@ public: Entry* entry(nullptr); { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); auto [resultIt, resultInserted] = table_.emplace( std::piecewise_construct, std::make_tuple(kindInbound, address.at_port(0)), // Key @@ -123,7 +123,7 @@ public: Entry* entry(nullptr); { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); auto [resultIt, resultInserted] = table_.emplace( std::piecewise_construct, std::make_tuple(kindOutbound, address), // Key @@ -156,7 +156,7 @@ public: Entry* entry(nullptr); { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); auto [resultIt, resultInserted] = table_.emplace( std::piecewise_construct, std::make_tuple(kindUnlimited, address.at_port(1)), // Key @@ -191,11 +191,11 @@ public: clock_type::time_point const now(m_clock.now()); Json::Value ret(Json::objectValue); - std::lock_guard _(lock_); + std::lock_guard const _(lock_); for (auto& inboundEntry : inbound_) { - int localBalance = inboundEntry.local_balance.value(now); + int const localBalance = inboundEntry.local_balance.value(now); if ((localBalance + inboundEntry.remote_balance) >= threshold) { Json::Value& entry = (ret[inboundEntry.to_string()] = Json::objectValue); @@ -206,7 +206,7 @@ public: } for (auto& outboundEntry : outbound_) { - int localBalance = outboundEntry.local_balance.value(now); + int const localBalance = outboundEntry.local_balance.value(now); if ((localBalance + outboundEntry.remote_balance) >= threshold) { Json::Value& entry = (ret[outboundEntry.to_string()] = Json::objectValue); @@ -217,7 +217,7 @@ public: } for (auto& adminEntry : admin_) { - int localBalance = adminEntry.local_balance.value(now); + int const localBalance = adminEntry.local_balance.value(now); if ((localBalance + adminEntry.remote_balance) >= threshold) { Json::Value& entry = (ret[adminEntry.to_string()] = Json::objectValue); @@ -236,7 +236,7 @@ public: clock_type::time_point const now(m_clock.now()); Gossip gossip; - std::lock_guard _(lock_); + std::lock_guard const _(lock_); gossip.items.reserve(inbound_.size()); @@ -261,7 +261,7 @@ public: { auto const elapsed = m_clock.now(); { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); auto [resultIt, resultInserted] = importTable_.emplace( std::piecewise_construct, std::make_tuple(origin), // Key @@ -318,7 +318,7 @@ public: void periodicActivity() { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); auto const elapsed = m_clock.now(); @@ -374,7 +374,7 @@ public: void erase(Table::iterator iter) { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); Entry& entry(iter->second); XRPL_ASSERT(entry.refcount == 0, "xrpl::Resource::Logic::erase : entry not used"); inactive_.erase(inactive_.iterator_to(entry)); @@ -384,14 +384,14 @@ public: void acquire(Entry& entry) { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); ++entry.refcount; } void release(Entry& entry) { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); if (--entry.refcount == 0) { JLOG(m_journal.debug()) << "Inactive " << entry; @@ -442,7 +442,7 @@ public: if (!context.empty()) context = " (" + context + ")"; - std::lock_guard _(lock_); + std::lock_guard const _(lock_); clock_type::time_point const now(m_clock.now()); int const balance(entry.add(fee.cost(), now)); JLOG(getStream(fee.cost(), m_journal)) << "Charging " << entry << " for " << fee << context; @@ -455,7 +455,7 @@ public: if (entry.isUnlimited()) return false; - std::lock_guard _(lock_); + std::lock_guard const _(lock_); bool notify(false); auto const elapsed = m_clock.now(); if (entry.balance(m_clock.now()) >= warningThreshold && elapsed != entry.lastWarningTime) @@ -478,7 +478,7 @@ public: if (entry.isUnlimited()) return false; - std::lock_guard _(lock_); + std::lock_guard const _(lock_); bool drop(false); clock_type::time_point const now(m_clock.now()); int const balance(entry.balance(now)); @@ -500,7 +500,7 @@ public: int balance(Entry& entry) { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); return entry.balance(m_clock.now()); } @@ -529,7 +529,7 @@ public: { clock_type::time_point const now(m_clock.now()); - std::lock_guard _(lock_); + std::lock_guard const _(lock_); { beast::PropertyStream::Set s("inbound", map); diff --git a/include/xrpl/server/InfoSub.h b/include/xrpl/server/InfoSub.h index 97cdf1aa35..d45f4d7740 100644 --- a/include/xrpl/server/InfoSub.h +++ b/include/xrpl/server/InfoSub.h @@ -173,7 +173,7 @@ public: send(Json::Value const& jvObj, bool broadcast) = 0; std::uint64_t - getSeq(); + getSeq() const; void onSendEmpty(); diff --git a/include/xrpl/server/LoadFeeTrack.h b/include/xrpl/server/LoadFeeTrack.h index fc031a1833..226408dbde 100644 --- a/include/xrpl/server/LoadFeeTrack.h +++ b/include/xrpl/server/LoadFeeTrack.h @@ -26,10 +26,6 @@ class LoadFeeTrack final public: explicit LoadFeeTrack(beast::Journal journal = beast::Journal(beast::Journal::getNullSink())) : j_(journal) - , localTxnLoadFee_(lftNormalFee) - , remoteTxnLoadFee_(lftNormalFee) - , clusterTxnLoadFee_(lftNormalFee) - , raiseCount_(0) { } @@ -39,28 +35,28 @@ public: setRemoteFee(std::uint32_t f) { JLOG(j_.trace()) << "setRemoteFee: " << f; - std::lock_guard sl(lock_); + std::lock_guard const sl(lock_); remoteTxnLoadFee_ = f; } std::uint32_t getRemoteFee() const { - std::lock_guard sl(lock_); + std::lock_guard const sl(lock_); return remoteTxnLoadFee_; } std::uint32_t getLocalFee() const { - std::lock_guard sl(lock_); + std::lock_guard const sl(lock_); return localTxnLoadFee_; } std::uint32_t getClusterFee() const { - std::lock_guard sl(lock_); + std::lock_guard const sl(lock_); return clusterTxnLoadFee_; } @@ -73,14 +69,14 @@ public: std::uint32_t getLoadFactor() const { - std::lock_guard sl(lock_); + std::lock_guard const sl(lock_); return std::max({clusterTxnLoadFee_, localTxnLoadFee_, remoteTxnLoadFee_}); } std::pair getScalingFactors() const { - std::lock_guard sl(lock_); + std::lock_guard const sl(lock_); return std::make_pair( std::max(localTxnLoadFee_, remoteTxnLoadFee_), @@ -91,7 +87,7 @@ public: setClusterFee(std::uint32_t fee) { JLOG(j_.trace()) << "setClusterFee: " << fee; - std::lock_guard sl(lock_); + std::lock_guard const sl(lock_); clusterTxnLoadFee_ = fee; } @@ -103,14 +99,14 @@ public: bool isLoadedLocal() const { - std::lock_guard sl(lock_); + std::lock_guard const sl(lock_); return (raiseCount_ != 0) || (localTxnLoadFee_ != lftNormalFee); } bool isLoadedCluster() const { - std::lock_guard sl(lock_); + std::lock_guard const sl(lock_); return (raiseCount_ != 0) || (localTxnLoadFee_ != lftNormalFee) || (clusterTxnLoadFee_ != lftNormalFee); } @@ -124,10 +120,10 @@ private: beast::Journal const j_; std::mutex mutable lock_; - std::uint32_t localTxnLoadFee_; // Scale factor, lftNormalFee = normal fee - std::uint32_t remoteTxnLoadFee_; // Scale factor, lftNormalFee = normal fee - std::uint32_t clusterTxnLoadFee_; // Scale factor, lftNormalFee = normal fee - std::uint32_t raiseCount_; + std::uint32_t localTxnLoadFee_{lftNormalFee}; // Scale factor, lftNormalFee = normal fee + std::uint32_t remoteTxnLoadFee_{lftNormalFee}; // Scale factor, lftNormalFee = normal fee + std::uint32_t clusterTxnLoadFee_{lftNormalFee}; // Scale factor, lftNormalFee = normal fee + std::uint32_t raiseCount_{0}; }; //------------------------------------------------------------------------------ diff --git a/include/xrpl/server/Manifest.h b/include/xrpl/server/Manifest.h index 7f1bba921e..2532a3c5bf 100644 --- a/include/xrpl/server/Manifest.h +++ b/include/xrpl/server/Manifest.h @@ -15,9 +15,9 @@ namespace xrpl { Validator key manifests ----------------------- - Suppose the secret keys installed on a Ripple validator are compromised. Not + Suppose the secret keys installed on an XRPL validator are compromised. Not only do you have to generate and install new key pairs on each validator, - EVERY rippled needs to have its config updated with the new public keys, and + EVERY xrpld needs to have its config updated with the new public keys, and is vulnerable to forged validation signatures until this is done. The solution is a new layer of indirection: A master secret key under restrictive access control is used to sign a "manifest": essentially, a @@ -39,11 +39,11 @@ namespace xrpl { seen for that validator, if any. On startup, the [validator_token] config entry (which contains the manifest for this validator) is decoded and added to the manifest cache. Other manifests are added as "gossip" - received from rippled peers. + received from xrpld peers. When an ephemeral key is compromised, a new signing key pair is created, along with a new manifest vouching for it (with a higher sequence number), - signed by the master key. When a rippled peer receives the new manifest, + signed by the master key. When an xrpld peer receives the new manifest, it verifies it with the master key and (assuming it's valid) discards the old ephemeral key and stores the new one. If the master key itself gets compromised, a manifest with sequence number 0xFFFFFFFF will supersede a @@ -401,7 +401,7 @@ public: void for_each_manifest(Function&& f) const { - std::shared_lock lock{mutex_}; + std::shared_lock const lock{mutex_}; for (auto const& [_, manifest] : map_) { (void)_; @@ -429,7 +429,7 @@ public: void for_each_manifest(PreFun&& pf, EachFun&& f) const { - std::shared_lock lock{mutex_}; + std::shared_lock const lock{mutex_}; pf(map_.size()); for (auto const& [_, manifest] : map_) { diff --git a/include/xrpl/server/NetworkOPs.h b/include/xrpl/server/NetworkOPs.h index 75f1e0e1b2..16ec4a4ec0 100644 --- a/include/xrpl/server/NetworkOPs.h +++ b/include/xrpl/server/NetworkOPs.h @@ -63,8 +63,8 @@ enum class OperatingMode { needed. A backend application or local client can trust a local instance of - rippled / NetworkOPs. However, client software connecting to non-local - instances of rippled will need to be hardened to protect against hostile + xrpld / NetworkOPs. However, client software connecting to non-local + instances of xrpld will need to be hardened to protect against hostile or unreliable servers. */ class NetworkOPs : public InfoSub::Source diff --git a/include/xrpl/server/Port.h b/include/xrpl/server/Port.h index 1dfa93b0a7..c7acabbc85 100644 --- a/include/xrpl/server/Port.h +++ b/include/xrpl/server/Port.h @@ -19,7 +19,7 @@ namespace boost { namespace asio { namespace ssl { class context; -} +} // namespace ssl } // namespace asio } // namespace boost @@ -54,7 +54,7 @@ struct Port int limit = 0; // Websocket disconnects if send queue exceeds this limit - std::uint16_t ws_queue_limit; + std::uint16_t ws_queue_limit{}; // Returns `true` if any websocket protocols are specified bool @@ -90,7 +90,7 @@ struct ParsedPort std::string ssl_ciphers; boost::beast::websocket::permessage_deflate pmd_options; int limit = 0; - std::uint16_t ws_queue_limit; + std::uint16_t ws_queue_limit{}; std::optional ip; std::optional port; diff --git a/include/xrpl/server/State.h b/include/xrpl/server/State.h index 48e11869f4..c3cc4f609c 100644 --- a/include/xrpl/server/State.h +++ b/include/xrpl/server/State.h @@ -12,7 +12,7 @@ struct SavedState { std::string writableDb; std::string archiveDb; - LedgerIndex lastRotated; + LedgerIndex lastRotated{}; }; /** diff --git a/include/xrpl/server/detail/BaseHTTPPeer.h b/include/xrpl/server/detail/BaseHTTPPeer.h index f900261b84..878f6a1ddf 100644 --- a/include/xrpl/server/detail/BaseHTTPPeer.h +++ b/include/xrpl/server/detail/BaseHTTPPeer.h @@ -48,14 +48,14 @@ protected: struct buffer { - buffer(void const* ptr, std::size_t len) : data(new char[len]), bytes(len), used(0) + buffer(void const* ptr, std::size_t len) : data(new char[len]), bytes(len) { memcpy(data.get(), ptr, len); } std::unique_ptr data; std::size_t bytes; - std::size_t used; + std::size_t used{0}; }; Port const& port_; @@ -300,7 +300,7 @@ BaseHTTPPeer::on_write(error_code const& ec, std::size_t bytes_tr return fail(ec, "write"); bytes_out_ += bytes_transferred; { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); wq2_.clear(); wq2_.reserve(wq_.size()); std::swap(wq2_, wq_); @@ -392,7 +392,7 @@ BaseHTTPPeer::write(void const* buf, std::size_t bytes) if (bytes == 0) return; if ([&] { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); wq_.emplace_back(buf, bytes); return wq_.size() == 1 && wq2_.size() == 0; }()) @@ -443,7 +443,7 @@ BaseHTTPPeer::complete() complete_ = true; { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); if (!wq_.empty() && !wq2_.empty()) return; } @@ -476,7 +476,7 @@ BaseHTTPPeer::close(bool graceful) { graceful_ = true; { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); if (!wq_.empty() || !wq2_.empty()) return; } diff --git a/include/xrpl/server/detail/BaseWSPeer.h b/include/xrpl/server/detail/BaseWSPeer.h index f415520e59..9213a955f0 100644 --- a/include/xrpl/server/detail/BaseWSPeer.h +++ b/include/xrpl/server/detail/BaseWSPeer.h @@ -418,7 +418,7 @@ BaseWSPeer::on_ping_pong( { if (kind == boost::beast::websocket::frame_type::pong) { - boost::beast::string_view p(payload_.begin()); + boost::beast::string_view const p(payload_.begin()); if (payload == p) { close_on_timer_ = false; diff --git a/include/xrpl/server/detail/Door.h b/include/xrpl/server/detail/Door.h index f977dc6002..87baad42db 100644 --- a/include/xrpl/server/detail/Door.h +++ b/include/xrpl/server/detail/Door.h @@ -88,8 +88,12 @@ private: boost::asio::io_context& ioc_; acceptor_type acceptor_; boost::asio::strand strand_; - bool ssl_; - bool plain_; + bool ssl_{ + port_.protocol.count("https") > 0 || port_.protocol.count("wss") > 0 || + port_.protocol.count("wss2") > 0 || port_.protocol.count("peer") > 0}; + bool plain_{ + port_.protocol.count("http") > 0 || port_.protocol.count("ws") > 0 || + port_.protocol.count("ws2")}; static constexpr std::chrono::milliseconds INITIAL_ACCEPT_DELAY{50}; static constexpr std::chrono::milliseconds MAX_ACCEPT_DELAY{2000}; std::chrono::milliseconds accept_delay_{INITIAL_ACCEPT_DELAY}; @@ -274,12 +278,6 @@ Door::Door( , ioc_(io_context) , acceptor_(io_context) , strand_(boost::asio::make_strand(io_context)) - , ssl_( - port_.protocol.count("https") > 0 || port_.protocol.count("wss") > 0 || - port_.protocol.count("wss2") > 0 || port_.protocol.count("peer") > 0) - , plain_( - port_.protocol.count("http") > 0 || port_.protocol.count("ws") > 0 || - port_.protocol.count("ws2")) , backoff_timer_(io_context) { reOpen(); @@ -397,7 +395,7 @@ Door::query_fd_stats() const return std::nullopt; #else FDStats s; - struct rlimit rl; + struct rlimit rl{}; if (getrlimit(RLIMIT_NOFILE, &rl) != 0 || rl.rlim_cur == RLIM_INFINITY) return std::nullopt; s.limit = static_cast(rl.rlim_cur); diff --git a/include/xrpl/server/detail/ServerImpl.h b/include/xrpl/server/detail/ServerImpl.h index 1f91413521..0b5d075d87 100644 --- a/include/xrpl/server/detail/ServerImpl.h +++ b/include/xrpl/server/detail/ServerImpl.h @@ -74,7 +74,7 @@ private: std::vector ports_; std::vector>> list_; int high_ = 0; - std::array hist_; + std::array hist_{}; io_list ios_; diff --git a/include/xrpl/server/detail/io_list.h b/include/xrpl/server/detail/io_list.h index 5b1a02e039..f1cc04f683 100644 --- a/include/xrpl/server/detail/io_list.h +++ b/include/xrpl/server/detail/io_list.h @@ -165,7 +165,7 @@ io_list::work::destroy() return; std::function f; { - std::lock_guard lock(ios_->m_); + std::lock_guard const lock(ios_->m_); ios_->map_.erase(this); if (--ios_->n_ == 0 && ios_->closed_) { @@ -195,7 +195,7 @@ io_list::emplace(Args&&... args) auto sp = std::make_shared(std::forward(args)...); decltype(sp) dead; - std::lock_guard lock(m_); + std::lock_guard const lock(m_); if (!closed_) { ++n_; diff --git a/include/xrpl/shamap/README.md b/include/xrpl/shamap/README.md index 419918c0cb..ae37d23ac3 100644 --- a/include/xrpl/shamap/README.md +++ b/include/xrpl/shamap/README.md @@ -112,7 +112,7 @@ When a `SHAMap` decides that it is safe to share a node of its own, it sets the node's sequence number to 0 (a `SHAMap` never has a sequence number of 0). This is done for every node in the trie when `SHAMap::walkSubTree` is executed. -Note that other objects in rippled also have sequence numbers (e.g. ledgers). +Note that other objects in xrpld also have sequence numbers (e.g. ledgers). The `SHAMap` and node sequence numbers should not be confused with these other sequence numbers (no relation). diff --git a/include/xrpl/shamap/SHAMap.h b/include/xrpl/shamap/SHAMap.h index 2c0910a830..213e7ce0ce 100644 --- a/include/xrpl/shamap/SHAMap.h +++ b/include/xrpl/shamap/SHAMap.h @@ -520,7 +520,7 @@ private: // getMissingNodes helper functions void gmn_ProcessNodes(MissingNodes&, MissingNodes::StackEntry& node); - void + static void gmn_ProcessDeferredReads(MissingNodes&); // fetch from DB helper function diff --git a/include/xrpl/shamap/SHAMapItem.h b/include/xrpl/shamap/SHAMapItem.h index dd098d88aa..da70e16db3 100644 --- a/include/xrpl/shamap/SHAMapItem.h +++ b/include/xrpl/shamap/SHAMapItem.h @@ -141,6 +141,7 @@ make_shamapitem(uint256 const& tag, Slice data) XRPL_ASSERT( data.size() <= megabytes(16), "xrpl::make_shamapitem : maximum input size"); + // NOLINTNEXTLINE(misc-const-correctness) std::uint8_t* raw = detail::slabber.allocate(data.size()); // If we can't grab memory from the slab allocators, we fall back to diff --git a/include/xrpl/tx/ApplyContext.h b/include/xrpl/tx/ApplyContext.h index 9e382556c2..6341c0bcc5 100644 --- a/include/xrpl/tx/ApplyContext.h +++ b/include/xrpl/tx/ApplyContext.h @@ -37,7 +37,7 @@ public: XRPL_ASSERT((flags & tapBATCH) == 0, "Batch apply flag should not be set"); } - ServiceRegistry& registry; + std::reference_wrapper registry; STTx const& tx; TER const preclaimResult; XRPAmount const baseFee; @@ -111,7 +111,7 @@ public: checkInvariants(TER const result, XRPAmount const fee); private: - TER + static TER failInvariantCheck(TER const result); template diff --git a/include/xrpl/tx/Transactor.h b/include/xrpl/tx/Transactor.h index 0e88cf92e1..287f785cd7 100644 --- a/include/xrpl/tx/Transactor.h +++ b/include/xrpl/tx/Transactor.h @@ -13,7 +13,7 @@ namespace xrpl { struct PreflightContext { public: - ServiceRegistry& registry; + std::reference_wrapper registry; STTx const& tx; Rules const rules; ApplyFlags flags; @@ -56,7 +56,7 @@ public: struct PreclaimContext { public: - ServiceRegistry& registry; + std::reference_wrapper registry; ReadView const& view; TER preflightResult; ApplyFlags flags; @@ -114,15 +114,15 @@ protected: beast::Journal const j_; AccountID const account_; - XRPAmount mPriorBalance; // Balance before fees. - XRPAmount mSourceBalance; // Balance after fees. + XRPAmount preFeeBalance_{}; // Balance before fees. - virtual ~Transactor() = default; Transactor(Transactor const&) = delete; Transactor& operator=(Transactor const&) = delete; public: + virtual ~Transactor() = default; + enum ConsequencesFactoryType { Normal, Blocker, Custom }; /** Process the transaction. */ ApplyResult @@ -210,7 +210,7 @@ public: checkPermission(ReadView const& view, STTx const& tx); ///////////////////////////////////////////////////// - // Interface used by DeleteAccount + // Interface used by AccountDelete static TER ticketDelete( ApplyView& view, diff --git a/include/xrpl/tx/applySteps.h b/include/xrpl/tx/applySteps.h index 2e76e20f5d..9ce6dfb1e2 100644 --- a/include/xrpl/tx/applySteps.h +++ b/include/xrpl/tx/applySteps.h @@ -213,7 +213,7 @@ public: , flags(ctx_.flags) , j(ctx_.j) , ter(ter_) - , likelyToClaimFee(ter == tesSUCCESS || isTecClaimHardFail(ter, flags)) + , likelyToClaimFee(isTesSuccess(ter) || isTecClaimHardFail(ter, flags)) { } diff --git a/include/xrpl/tx/invariants/AMMInvariant.h b/include/xrpl/tx/invariants/AMMInvariant.h index 63ebb804ae..e872c61f76 100644 --- a/include/xrpl/tx/invariants/AMMInvariant.h +++ b/include/xrpl/tx/invariants/AMMInvariant.h @@ -15,12 +15,12 @@ class ValidAMM std::optional ammAccount_; std::optional lptAMMBalanceAfter_; std::optional lptAMMBalanceBefore_; - bool ammPoolChanged_; + bool ammPoolChanged_{false}; public: enum class ZeroAllowed : bool { No = false, Yes = true }; - ValidAMM() : ammPoolChanged_{false} + ValidAMM() { } void diff --git a/include/xrpl/tx/invariants/FreezeInvariant.h b/include/xrpl/tx/invariants/FreezeInvariant.h index ac9d83166e..645f444462 100644 --- a/include/xrpl/tx/invariants/FreezeInvariant.h +++ b/include/xrpl/tx/invariants/FreezeInvariant.h @@ -48,7 +48,7 @@ private: bool isValidEntry(std::shared_ptr const& before, std::shared_ptr const& after); - STAmount + static STAmount calculateBalanceChange( std::shared_ptr const& before, std::shared_ptr const& after, @@ -63,7 +63,7 @@ private: std::shared_ptr findIssuer(AccountID const& issuerID, ReadView const& view); - bool + static bool validateIssuerChanges( std::shared_ptr const& issuer, IssuerChanges const& changes, @@ -71,7 +71,7 @@ private: beast::Journal const& j, bool enforce); - bool + static bool validateFrozenState( BalanceChange const& change, bool high, diff --git a/include/xrpl/tx/invariants/InvariantCheck.h b/include/xrpl/tx/invariants/InvariantCheck.h index 5ded5980da..e7ced63785 100644 --- a/include/xrpl/tx/invariants/InvariantCheck.h +++ b/include/xrpl/tx/invariants/InvariantCheck.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,33 @@ namespace xrpl { * communicate the interface required of any invariant checker. Any invariant * check implementation should implement the public methods documented here. * + * ## Rules for implementing `finalize` + * + * ### Invariants must run regardless of transaction result + * + * An invariant's `finalize` method MUST perform meaningful checks even when + * the transaction has failed (i.e., `!isTesSuccess(tec)`). The following + * pattern is almost certainly wrong and must never be used: + * + * @code + * // WRONG: skipping all checks on failure defeats the purpose of invariants + * if (!isTesSuccess(tec)) + * return true; + * @endcode + * + * The entire purpose of invariants is to detect and prevent the impossible. + * A bug or exploit could cause a failed transaction to mutate ledger state in + * unexpected ways. Invariants are the last line of defense against such + * scenarios. + * + * In general: an invariant that expects a domain-specific state change to + * occur (e.g., a new object being created) should only expect that change + * when the transaction succeeded. A failed VaultCreate must not have created + * a Vault. A failed LoanSet must not have created a Loan. + * + * Also be aware that failed transactions, regardless of type, carry no + * Privileges. Any privilege-gated checks must therefore also be applied to + * failed transactions. */ class InvariantChecker_PROTOTYPE { @@ -48,7 +76,11 @@ public: /** * @brief called after all ledger entries have been visited to determine - * the final status of the check + * the final status of the check. + * + * This method MUST perform meaningful checks even when `tec` indicates a + * failed transaction. See the class-level documentation for the rules + * governing how failed transactions must be handled. * * @param tx the transaction being applied * @param tec the current TER result of the transaction @@ -80,7 +112,7 @@ public: void visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); - bool + static bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); }; @@ -101,7 +133,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -121,7 +153,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -167,7 +199,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -184,7 +216,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -202,7 +234,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -221,7 +253,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -240,7 +272,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -256,7 +288,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -276,7 +308,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -297,7 +329,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** diff --git a/include/xrpl/tx/invariants/LoanBrokerInvariant.h b/include/xrpl/tx/invariants/LoanBrokerInvariant.h new file mode 100644 index 0000000000..e7d14a638b --- /dev/null +++ b/include/xrpl/tx/invariants/LoanBrokerInvariant.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +/** + * @brief Invariants: Loan brokers are internally consistent + * + * 1. If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most one + * node (the root), which will only hold entries for `RippleState` or + * `MPToken` objects. + * + */ +class ValidLoanBroker +{ + // Not all of these elements will necessarily be populated. Remaining items + // will be looked up as needed. + struct BrokerInfo + { + SLE::const_pointer brokerBefore = nullptr; + // After is used for most of the checks, except + // those that check changed values. + SLE::const_pointer brokerAfter = nullptr; + }; + // Collect all the LoanBrokers found directly or indirectly through + // pseudo-accounts. Key is the brokerID / index. It will be used to find the + // LoanBroker object if brokerBefore and brokerAfter are nullptr + std::map brokers_; + // Collect all the modified trust lines. Their high and low accounts will be + // loaded to look for LoanBroker pseudo-accounts. + std::vector lines_; + // Collect all the modified MPTokens. Their accounts will be loaded to look + // for LoanBroker pseudo-accounts. + std::vector mpts_; + + static bool + goodZeroDirectory(ReadView const& view, SLE::const_ref dir, beast::Journal const& j); + +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/LoanInvariant.h b/include/xrpl/tx/invariants/LoanInvariant.h index be771cd582..bda9c51653 100644 --- a/include/xrpl/tx/invariants/LoanInvariant.h +++ b/include/xrpl/tx/invariants/LoanInvariant.h @@ -1,57 +1,14 @@ #pragma once -#include #include #include #include #include -#include #include namespace xrpl { -/** - * @brief Invariants: Loan brokers are internally consistent - * - * 1. If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most one - * node (the root), which will only hold entries for `RippleState` or - * `MPToken` objects. - * - */ -class ValidLoanBroker -{ - // Not all of these elements will necessarily be populated. Remaining items - // will be looked up as needed. - struct BrokerInfo - { - SLE::const_pointer brokerBefore = nullptr; - // After is used for most of the checks, except - // those that check changed values. - SLE::const_pointer brokerAfter = nullptr; - }; - // Collect all the LoanBrokers found directly or indirectly through - // pseudo-accounts. Key is the brokerID / index. It will be used to find the - // LoanBroker object if brokerBefore and brokerAfter are nullptr - std::map brokers_; - // Collect all the modified trust lines. Their high and low accounts will be - // loaded to look for LoanBroker pseudo-accounts. - std::vector lines_; - // Collect all the modified MPTokens. Their accounts will be loaded to look - // for LoanBroker pseudo-accounts. - std::vector mpts_; - - bool - goodZeroDirectory(ReadView const& view, SLE::const_ref dir, beast::Journal const& j) const; - -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&); -}; - /** * @brief Invariants: Loans are internally consistent * diff --git a/include/xrpl/tx/invariants/MPTInvariant.h b/include/xrpl/tx/invariants/MPTInvariant.h index b6533c263d..68f866d362 100644 --- a/include/xrpl/tx/invariants/MPTInvariant.h +++ b/include/xrpl/tx/invariants/MPTInvariant.h @@ -25,7 +25,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; } // namespace xrpl diff --git a/include/xrpl/tx/invariants/NFTInvariant.h b/include/xrpl/tx/invariants/NFTInvariant.h index 8a88ca1c63..5bb5f90437 100644 --- a/include/xrpl/tx/invariants/NFTInvariant.h +++ b/include/xrpl/tx/invariants/NFTInvariant.h @@ -36,7 +36,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -64,7 +64,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; } // namespace xrpl diff --git a/src/xrpld/app/paths/AMMLiquidity.h b/include/xrpl/tx/paths/AMMLiquidity.h similarity index 97% rename from src/xrpld/app/paths/AMMLiquidity.h rename to include/xrpl/tx/paths/AMMLiquidity.h index 71b8dbb12a..87d6ffe32f 100644 --- a/src/xrpld/app/paths/AMMLiquidity.h +++ b/include/xrpl/tx/paths/AMMLiquidity.h @@ -3,10 +3,10 @@ #include #include #include +#include +#include #include #include -#include -#include namespace xrpl { diff --git a/src/xrpld/app/paths/AMMOffer.h b/include/xrpl/tx/paths/AMMOffer.h similarity index 97% rename from src/xrpld/app/paths/AMMOffer.h rename to include/xrpl/tx/paths/AMMOffer.h index ebaa7311c0..4ef1b2048b 100644 --- a/src/xrpld/app/paths/AMMOffer.h +++ b/include/xrpl/tx/paths/AMMOffer.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -36,7 +37,7 @@ private: // else the amounts quality Quality const quality_; // AMM offer can be consumed once at a given iteration - bool consumed_; + bool consumed_{false}; public: AMMOffer( diff --git a/include/xrpl/tx/paths/BookTip.h b/include/xrpl/tx/paths/BookTip.h index 69d3ec70d8..6a1805e83a 100644 --- a/include/xrpl/tx/paths/BookTip.h +++ b/include/xrpl/tx/paths/BookTip.h @@ -16,13 +16,13 @@ class BookTip { private: ApplyView& view_; - bool m_valid; + bool m_valid{false}; uint256 m_book; uint256 m_end; uint256 m_dir; uint256 m_index; std::shared_ptr m_entry; - Quality m_quality; + Quality m_quality{}; public: /** Create the iterator. */ diff --git a/include/xrpl/tx/paths/Flow.h b/include/xrpl/tx/paths/Flow.h index 32e8c3611b..f37c9d56c3 100644 --- a/include/xrpl/tx/paths/Flow.h +++ b/include/xrpl/tx/paths/Flow.h @@ -9,7 +9,7 @@ namespace xrpl { namespace path { namespace detail { struct FlowDebugInfo; -} +} // namespace detail } // namespace path /** diff --git a/include/xrpl/tx/paths/Offer.h b/include/xrpl/tx/paths/Offer.h index 4b161c5f2d..de3eed933a 100644 --- a/include/xrpl/tx/paths/Offer.h +++ b/include/xrpl/tx/paths/Offer.h @@ -32,10 +32,10 @@ class TOffer : private TOfferBase { private: SLE::pointer m_entry; - Quality m_quality; + Quality m_quality{}; AccountID m_account; - TAmounts m_amounts; + TAmounts m_amounts{}; void setFieldAmounts(); diff --git a/include/xrpl/tx/paths/OfferStream.h b/include/xrpl/tx/paths/OfferStream.h index df96a1b6da..ba553360ef 100644 --- a/include/xrpl/tx/paths/OfferStream.h +++ b/include/xrpl/tx/paths/OfferStream.h @@ -19,11 +19,11 @@ public: { private: std::uint32_t const limit_; - std::uint32_t count_; + std::uint32_t count_{0}; beast::Journal j_; public: - StepCounter(std::uint32_t limit, beast::Journal j) : limit_(limit), count_(0), j_(j) + StepCounter(std::uint32_t limit, beast::Journal j) : limit_(limit), j_(j) { } diff --git a/include/xrpl/tx/paths/RippleCalc.h b/include/xrpl/tx/paths/RippleCalc.h index a5cecc18bf..55f552a61f 100644 --- a/include/xrpl/tx/paths/RippleCalc.h +++ b/include/xrpl/tx/paths/RippleCalc.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include #include @@ -13,7 +13,7 @@ namespace path { namespace detail { struct FlowDebugInfo; -} +} // namespace detail /** RippleCalc calculates the quality of a payment path. @@ -92,7 +92,7 @@ public: STPathSet const& spsPaths, std::optional const& domainID, - Logs& l, + ServiceRegistry& registry, Input const* const pInputs = nullptr); // The view we are currently working on diff --git a/include/xrpl/tx/paths/detail/AmountSpec.h b/include/xrpl/tx/paths/detail/AmountSpec.h index 690e67ae22..1adee6a0a3 100644 --- a/include/xrpl/tx/paths/detail/AmountSpec.h +++ b/include/xrpl/tx/paths/detail/AmountSpec.h @@ -12,7 +12,7 @@ struct AmountSpec { explicit AmountSpec() = default; - bool native; + bool native{}; union { XRPAmount xrp; diff --git a/src/xrpld/app/paths/detail/StepChecks.h b/include/xrpl/tx/paths/detail/StepChecks.h similarity index 100% rename from src/xrpld/app/paths/detail/StepChecks.h rename to include/xrpl/tx/paths/detail/StepChecks.h diff --git a/include/xrpl/tx/paths/detail/Steps.h b/include/xrpl/tx/paths/detail/Steps.h index 53c9294155..0b75974acb 100644 --- a/include/xrpl/tx/paths/detail/Steps.h +++ b/include/xrpl/tx/paths/detail/Steps.h @@ -147,29 +147,26 @@ public: /** If this step is a DirectStepI, return the quality in of the dst account. - */ + */ virtual std::uint32_t lineQualityIn(ReadView const&) const { return QUALITY_ONE; } - // clang-format off /** Find an upper bound of quality for the step @param v view to query the ledger state from @param prevStepDir Set to DebtDirection::redeems if the previous step redeems. - @return A pair. The first element is the upper bound of quality for the step, or std::nullopt if the - step is dry. The second element will be set to DebtDirection::redeems if this steps redeems, - DebtDirection:issues if this step issues. - @note it is an upper bound because offers on the books may be unfunded. - If there is always a funded offer at the tip of the book, then we could - rename this `theoreticalQuality` rather than `qualityUpperBound`. It - could still differ from the actual quality, but except for "dust" amounts, - it should be a good estimate for the actual quality. - */ - // clang-format on + @return A pair. The first element is the upper bound of quality for the step, or std::nullopt + if the step is dry. The second element will be set to DebtDirection::redeems if this + steps redeems, DebtDirection:issues if this step issues. + @note It is an upper bound because offers on the books may be unfunded. If there is always a + funded offer at the tip of the book, then we could rename this `theoreticalQuality` + rather than `qualityUpperBound`. It could still differ from the actual quality, but + except for "dust" amounts, it should be a good estimate for the actual quality. + */ virtual std::pair, DebtDirection> qualityUpperBound(ReadView const& v, DebtDirection prevStepDir) const = 0; diff --git a/include/xrpl/tx/paths/detail/StrandFlow.h b/include/xrpl/tx/paths/detail/StrandFlow.h index 67e333f2e6..1f05ae1a5b 100644 --- a/include/xrpl/tx/paths/detail/StrandFlow.h +++ b/include/xrpl/tx/paths/detail/StrandFlow.h @@ -1,7 +1,10 @@ #pragma once #include -#include +#include +#include +#include +#include #include #include #include @@ -11,7 +14,6 @@ #include #include #include -#include #include @@ -25,7 +27,7 @@ namespace xrpl { template struct StrandResult { - bool success; ///< Strand succeeded + bool success = false; ///< Strand succeeded TInAmt in = beast::zero; ///< Currency amount in TOutAmt out = beast::zero; ///< Currency amount out std::optional sandbox; ///< Resulting Sandbox state @@ -59,7 +61,7 @@ struct StrandResult } StrandResult(Strand const& strand, boost::container::flat_set ofrsToRm_) - : success(false), ofrsToRm(std::move(ofrsToRm_)), ofrsUsed(offersUsed(strand)) + : ofrsToRm(std::move(ofrsToRm_)), ofrsUsed(offersUsed(strand)) { } }; @@ -569,7 +571,7 @@ flow( std::size_t const maxTries = 1000; std::size_t curTry = 0; - std::uint32_t maxOffersToConsider = 1500; + std::uint32_t const maxOffersToConsider = 1500; std::uint32_t offersConsidered = 0; // There is a bug in gcc that incorrectly warns about using uninitialized @@ -769,7 +771,7 @@ flow( { // Rounding in the payment engine is causing this assert to // sometimes fire with "dust" amounts. This is causing issues when - // running debug builds of rippled. While this issue still needs to + // running debug builds of xrpld. While this issue still needs to // be resolved, the assert is causing more harm than good at this // point. // UNREACHABLE("xrpl::flow : rounding error"); diff --git a/include/xrpl/tx/transactors/account/DeleteAccount.h b/include/xrpl/tx/transactors/account/AccountDelete.h similarity index 77% rename from include/xrpl/tx/transactors/account/DeleteAccount.h rename to include/xrpl/tx/transactors/account/AccountDelete.h index f55888ee00..81a11152ed 100644 --- a/include/xrpl/tx/transactors/account/DeleteAccount.h +++ b/include/xrpl/tx/transactors/account/AccountDelete.h @@ -4,12 +4,12 @@ namespace xrpl { -class DeleteAccount : public Transactor +class AccountDelete : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Blocker}; - explicit DeleteAccount(ApplyContext& ctx) : Transactor(ctx) + explicit AccountDelete(ApplyContext& ctx) : Transactor(ctx) { } @@ -29,6 +29,4 @@ public: doApply() override; }; -using AccountDelete = DeleteAccount; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/account/SetAccount.h b/include/xrpl/tx/transactors/account/AccountSet.h similarity index 82% rename from include/xrpl/tx/transactors/account/SetAccount.h rename to include/xrpl/tx/transactors/account/AccountSet.h index 3abfcb43bb..0940ab0739 100644 --- a/include/xrpl/tx/transactors/account/SetAccount.h +++ b/include/xrpl/tx/transactors/account/AccountSet.h @@ -5,12 +5,12 @@ namespace xrpl { -class SetAccount : public Transactor +class AccountSet : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; - explicit SetAccount(ApplyContext& ctx) : Transactor(ctx) + explicit AccountSet(ApplyContext& ctx) : Transactor(ctx) { } @@ -33,6 +33,4 @@ public: doApply() override; }; -using AccountSet = SetAccount; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/account/SetSignerList.h b/include/xrpl/tx/transactors/account/SignerListSet.h similarity index 86% rename from include/xrpl/tx/transactors/account/SetSignerList.h rename to include/xrpl/tx/transactors/account/SignerListSet.h index 4c2489922d..2529f0d36c 100644 --- a/include/xrpl/tx/transactors/account/SetSignerList.h +++ b/include/xrpl/tx/transactors/account/SignerListSet.h @@ -11,10 +11,10 @@ namespace xrpl { /** -See the README.md for an overview of the SetSignerList transaction that +See the README.md for an overview of the SignerListSet transaction that this class implements. */ -class SetSignerList : public Transactor +class SignerListSet : public Transactor { private: // Values determined during preCompute for use later. @@ -26,7 +26,7 @@ private: public: static constexpr ConsequencesFactoryType ConsequencesFactory{Blocker}; - explicit SetSignerList(ApplyContext& ctx) : Transactor(ctx) + explicit SignerListSet(ApplyContext& ctx) : Transactor(ctx) { } @@ -41,7 +41,7 @@ public: void preCompute() override; - // Interface used by DeleteAccount + // Interface used by AccountDelete static TER removeFromLedger( ServiceRegistry& registry, @@ -70,6 +70,4 @@ private: writeSignersToSLE(SLE::pointer const& ledgerEntry, std::uint32_t flags) const; }; -using SignerListSet = SetSignerList; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/check/CashCheck.h b/include/xrpl/tx/transactors/check/CheckCancel.h similarity index 73% rename from include/xrpl/tx/transactors/check/CashCheck.h rename to include/xrpl/tx/transactors/check/CheckCancel.h index 138790cf34..f16c6d2827 100644 --- a/include/xrpl/tx/transactors/check/CashCheck.h +++ b/include/xrpl/tx/transactors/check/CheckCancel.h @@ -4,12 +4,12 @@ namespace xrpl { -class CashCheck : public Transactor +class CheckCancel : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit CashCheck(ApplyContext& ctx) : Transactor(ctx) + explicit CheckCancel(ApplyContext& ctx) : Transactor(ctx) { } @@ -23,6 +23,4 @@ public: doApply() override; }; -using CheckCash = CashCheck; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/check/CreateCheck.h b/include/xrpl/tx/transactors/check/CheckCash.h similarity index 71% rename from include/xrpl/tx/transactors/check/CreateCheck.h rename to include/xrpl/tx/transactors/check/CheckCash.h index 98950b68f2..c64d45061d 100644 --- a/include/xrpl/tx/transactors/check/CreateCheck.h +++ b/include/xrpl/tx/transactors/check/CheckCash.h @@ -4,12 +4,12 @@ namespace xrpl { -class CreateCheck : public Transactor +class CheckCash : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit CreateCheck(ApplyContext& ctx) : Transactor(ctx) + explicit CheckCash(ApplyContext& ctx) : Transactor(ctx) { } @@ -23,6 +23,4 @@ public: doApply() override; }; -using CheckCreate = CreateCheck; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/check/CancelCheck.h b/include/xrpl/tx/transactors/check/CheckCreate.h similarity index 71% rename from include/xrpl/tx/transactors/check/CancelCheck.h rename to include/xrpl/tx/transactors/check/CheckCreate.h index 8a0e42c7ca..2b5aa9123d 100644 --- a/include/xrpl/tx/transactors/check/CancelCheck.h +++ b/include/xrpl/tx/transactors/check/CheckCreate.h @@ -4,12 +4,12 @@ namespace xrpl { -class CancelCheck : public Transactor +class CheckCreate : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit CancelCheck(ApplyContext& ctx) : Transactor(ctx) + explicit CheckCreate(ApplyContext& ctx) : Transactor(ctx) { } @@ -23,6 +23,4 @@ public: doApply() override; }; -using CheckCancel = CancelCheck; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/credentials/CredentialAccept.h b/include/xrpl/tx/transactors/credentials/CredentialAccept.h new file mode 100644 index 0000000000..1895e54589 --- /dev/null +++ b/include/xrpl/tx/transactors/credentials/CredentialAccept.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace xrpl { + +class CredentialAccept : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit CredentialAccept(ApplyContext& ctx) : Transactor(ctx) + { + } + + static std::uint32_t + getFlagsMask(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/credentials/CredentialCreate.h b/include/xrpl/tx/transactors/credentials/CredentialCreate.h new file mode 100644 index 0000000000..500b034a9c --- /dev/null +++ b/include/xrpl/tx/transactors/credentials/CredentialCreate.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace xrpl { + +class CredentialCreate : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit CredentialCreate(ApplyContext& ctx) : Transactor(ctx) + { + } + + static std::uint32_t + getFlagsMask(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/credentials/CredentialDelete.h b/include/xrpl/tx/transactors/credentials/CredentialDelete.h new file mode 100644 index 0000000000..26305232ce --- /dev/null +++ b/include/xrpl/tx/transactors/credentials/CredentialDelete.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace xrpl { + +class CredentialDelete : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit CredentialDelete(ApplyContext& ctx) : Transactor(ctx) + { + } + + static std::uint32_t + getFlagsMask(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/credentials/Credentials.h b/include/xrpl/tx/transactors/credentials/Credentials.h deleted file mode 100644 index 8c96f40526..0000000000 --- a/include/xrpl/tx/transactors/credentials/Credentials.h +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include - -namespace xrpl { - -class CredentialCreate : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - - explicit CredentialCreate(ApplyContext& ctx) : Transactor(ctx) - { - } - - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - - static NotTEC - preflight(PreflightContext const& ctx); - - static TER - preclaim(PreclaimContext const& ctx); - - TER - doApply() override; -}; - -//------------------------------------------------------------------------------ - -class CredentialDelete : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - - explicit CredentialDelete(ApplyContext& ctx) : Transactor(ctx) - { - } - - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - - static NotTEC - preflight(PreflightContext const& ctx); - - static TER - preclaim(PreclaimContext const& ctx); - - TER - doApply() override; -}; - -//------------------------------------------------------------------------------ - -class CredentialAccept : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - - explicit CredentialAccept(ApplyContext& ctx) : Transactor(ctx) - { - } - - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - - static NotTEC - preflight(PreflightContext const& ctx); - - static TER - preclaim(PreclaimContext const& ctx); - - TER - doApply() override; -}; - -} // namespace xrpl diff --git a/include/xrpl/tx/transactors/delegate/DelegateSet.h b/include/xrpl/tx/transactors/delegate/DelegateSet.h index 452a6d8305..cc3a8fe4cb 100644 --- a/include/xrpl/tx/transactors/delegate/DelegateSet.h +++ b/include/xrpl/tx/transactors/delegate/DelegateSet.h @@ -22,7 +22,7 @@ public: TER doApply() override; - // Interface used by DeleteAccount + // Interface used by AccountDelete static TER deleteDelegate( ApplyView& view, diff --git a/include/xrpl/tx/transactors/dex/AMMWithdraw.h b/include/xrpl/tx/transactors/dex/AMMWithdraw.h index c15bb68644..5328b03abb 100644 --- a/include/xrpl/tx/transactors/dex/AMMWithdraw.h +++ b/include/xrpl/tx/transactors/dex/AMMWithdraw.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace xrpl { diff --git a/include/xrpl/tx/transactors/dex/CancelOffer.h b/include/xrpl/tx/transactors/dex/OfferCancel.h similarity index 73% rename from include/xrpl/tx/transactors/dex/CancelOffer.h rename to include/xrpl/tx/transactors/dex/OfferCancel.h index 6107c3211f..c4c57ff3b4 100644 --- a/include/xrpl/tx/transactors/dex/CancelOffer.h +++ b/include/xrpl/tx/transactors/dex/OfferCancel.h @@ -5,12 +5,12 @@ namespace xrpl { -class CancelOffer : public Transactor +class OfferCancel : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit CancelOffer(ApplyContext& ctx) : Transactor(ctx) + explicit OfferCancel(ApplyContext& ctx) : Transactor(ctx) { } @@ -24,6 +24,4 @@ public: doApply() override; }; -using OfferCancel = CancelOffer; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/dex/CreateOffer.h b/include/xrpl/tx/transactors/dex/OfferCreate.h similarity index 93% rename from include/xrpl/tx/transactors/dex/CreateOffer.h rename to include/xrpl/tx/transactors/dex/OfferCreate.h index cb15d08ace..2179180054 100644 --- a/include/xrpl/tx/transactors/dex/CreateOffer.h +++ b/include/xrpl/tx/transactors/dex/OfferCreate.h @@ -9,13 +9,13 @@ class PaymentSandbox; class Sandbox; /** Transactor specialized for creating offers in the ledger. */ -class CreateOffer : public Transactor +class OfferCreate : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; /** Construct a Transactor subclass that creates an offer in the ledger. */ - explicit CreateOffer(ApplyContext& ctx) : Transactor(ctx) + explicit OfferCreate(ApplyContext& ctx) : Transactor(ctx) { } @@ -74,6 +74,4 @@ private: std::function)> const& setDir); }; -using OfferCreate = CreateOffer; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/did/DID.h b/include/xrpl/tx/transactors/did/DIDDelete.h similarity index 61% rename from include/xrpl/tx/transactors/did/DID.h rename to include/xrpl/tx/transactors/did/DIDDelete.h index 8ab80bc694..037a57bb02 100644 --- a/include/xrpl/tx/transactors/did/DID.h +++ b/include/xrpl/tx/transactors/did/DIDDelete.h @@ -4,24 +4,6 @@ namespace xrpl { -class DIDSet : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - - explicit DIDSet(ApplyContext& ctx) : Transactor(ctx) - { - } - - static NotTEC - preflight(PreflightContext const& ctx); - - TER - doApply() override; -}; - -//------------------------------------------------------------------------------ - class DIDDelete : public Transactor { public: diff --git a/include/xrpl/tx/transactors/did/DIDSet.h b/include/xrpl/tx/transactors/did/DIDSet.h new file mode 100644 index 0000000000..fe1c476b47 --- /dev/null +++ b/include/xrpl/tx/transactors/did/DIDSet.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace xrpl { + +class DIDSet : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit DIDSet(ApplyContext& ctx) : Transactor(ctx) + { + } + + static NotTEC + preflight(PreflightContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/escrow/Escrow.h b/include/xrpl/tx/transactors/escrow/Escrow.h deleted file mode 100644 index 0fd627e5a9..0000000000 --- a/include/xrpl/tx/transactors/escrow/Escrow.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -namespace xrpl { - -class EscrowCreate : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; - - explicit EscrowCreate(ApplyContext& ctx) : Transactor(ctx) - { - } - - static TxConsequences - makeTxConsequences(PreflightContext const& ctx); - - static NotTEC - preflight(PreflightContext const& ctx); - - static TER - preclaim(PreclaimContext const& ctx); - - TER - doApply() override; -}; - -//------------------------------------------------------------------------------ - -class EscrowFinish : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - - explicit EscrowFinish(ApplyContext& ctx) : Transactor(ctx) - { - } - - static bool - checkExtraFeatures(PreflightContext const& ctx); - - static NotTEC - preflight(PreflightContext const& ctx); - - static NotTEC - preflightSigValidated(PreflightContext const& ctx); - - static XRPAmount - calculateBaseFee(ReadView const& view, STTx const& tx); - - static TER - preclaim(PreclaimContext const& ctx); - - TER - doApply() override; -}; - -//------------------------------------------------------------------------------ - -class EscrowCancel : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - - explicit EscrowCancel(ApplyContext& ctx) : Transactor(ctx) - { - } - - static NotTEC - preflight(PreflightContext const& ctx); - - static TER - preclaim(PreclaimContext const& ctx); - - TER - doApply() override; -}; - -} // namespace xrpl diff --git a/include/xrpl/tx/transactors/escrow/EscrowCancel.h b/include/xrpl/tx/transactors/escrow/EscrowCancel.h new file mode 100644 index 0000000000..c8b97203f0 --- /dev/null +++ b/include/xrpl/tx/transactors/escrow/EscrowCancel.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace xrpl { + +class EscrowCancel : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit EscrowCancel(ApplyContext& ctx) : Transactor(ctx) + { + } + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/escrow/EscrowCreate.h b/include/xrpl/tx/transactors/escrow/EscrowCreate.h new file mode 100644 index 0000000000..221ffee070 --- /dev/null +++ b/include/xrpl/tx/transactors/escrow/EscrowCreate.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace xrpl { + +class EscrowCreate : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; + + explicit EscrowCreate(ApplyContext& ctx) : Transactor(ctx) + { + } + + static TxConsequences + makeTxConsequences(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/escrow/EscrowFinish.h b/include/xrpl/tx/transactors/escrow/EscrowFinish.h new file mode 100644 index 0000000000..0917c35d93 --- /dev/null +++ b/include/xrpl/tx/transactors/escrow/EscrowFinish.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace xrpl { + +class EscrowFinish : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit EscrowFinish(ApplyContext& ctx) : Transactor(ctx) + { + } + + static bool + checkExtraFeatures(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + static NotTEC + preflightSigValidated(PreflightContext const& ctx); + + static XRPAmount + calculateBaseFee(ReadView const& view, STTx const& tx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/lending/LendingHelpers.h b/include/xrpl/tx/transactors/lending/LendingHelpers.h index 4057c9c173..897ca3995b 100644 --- a/include/xrpl/tx/transactors/lending/LendingHelpers.h +++ b/include/xrpl/tx/transactors/lending/LendingHelpers.h @@ -143,7 +143,7 @@ struct LoanProperties // - A minimum scale required to represent the periodic payment accurately // All loan state values (principal, interest, fees) are rounded to this // scale. - std::int32_t loanScale; + std::int32_t loanScale{}; // The principal portion of the first payment. Number firstPaymentPrincipal; diff --git a/include/xrpl/tx/transactors/nft/NFTokenMint.h b/include/xrpl/tx/transactors/nft/NFTokenMint.h index d4eeba2bf0..d04f88ed3b 100644 --- a/include/xrpl/tx/transactors/nft/NFTokenMint.h +++ b/include/xrpl/tx/transactors/nft/NFTokenMint.h @@ -1,8 +1,8 @@ #pragma once +#include #include #include -#include namespace xrpl { diff --git a/include/xrpl/tx/transactors/oracle/DeleteOracle.h b/include/xrpl/tx/transactors/oracle/OracleDelete.h similarity index 79% rename from include/xrpl/tx/transactors/oracle/DeleteOracle.h rename to include/xrpl/tx/transactors/oracle/OracleDelete.h index bff33205aa..9d1e6d3a44 100644 --- a/include/xrpl/tx/transactors/oracle/DeleteOracle.h +++ b/include/xrpl/tx/transactors/oracle/OracleDelete.h @@ -10,15 +10,15 @@ namespace xrpl { to decentralized applications (dApps) on the blockchain. This implementation conforms to the requirements specified in the XLS-47d. - The DeleteOracle transactor implements the deletion of Oracle objects. + The OracleDelete transactor implements the deletion of Oracle objects. */ -class DeleteOracle : public Transactor +class OracleDelete : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit DeleteOracle(ApplyContext& ctx) : Transactor(ctx) + explicit OracleDelete(ApplyContext& ctx) : Transactor(ctx) { } @@ -39,6 +39,4 @@ public: beast::Journal j); }; -using OracleDelete = DeleteOracle; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/oracle/SetOracle.h b/include/xrpl/tx/transactors/oracle/OracleSet.h similarity index 77% rename from include/xrpl/tx/transactors/oracle/SetOracle.h rename to include/xrpl/tx/transactors/oracle/OracleSet.h index be1a7b8821..d879d14a84 100644 --- a/include/xrpl/tx/transactors/oracle/SetOracle.h +++ b/include/xrpl/tx/transactors/oracle/OracleSet.h @@ -10,15 +10,15 @@ namespace xrpl { to decentralized applications (dApps) on the blockchain. This implementation conforms to the requirements specified in the XLS-47d. - The SetOracle transactor implements creating or updating Oracle objects. + The OracleSet transactor implements creating or updating Oracle objects. */ -class SetOracle : public Transactor +class OracleSet : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit SetOracle(ApplyContext& ctx) : Transactor(ctx) + explicit OracleSet(ApplyContext& ctx) : Transactor(ctx) { } @@ -32,6 +32,4 @@ public: doApply() override; }; -using OracleSet = SetOracle; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/payment/DepositPreauth.h b/include/xrpl/tx/transactors/payment/DepositPreauth.h index 24050da600..b397a81748 100644 --- a/include/xrpl/tx/transactors/payment/DepositPreauth.h +++ b/include/xrpl/tx/transactors/payment/DepositPreauth.h @@ -25,7 +25,7 @@ public: TER doApply() override; - // Interface used by DeleteAccount + // Interface used by AccountDelete static TER removeFromLedger(ApplyView& view, uint256 const& delIndex, beast::Journal j); }; diff --git a/include/xrpl/tx/transactors/payment_channel/PayChan.h b/include/xrpl/tx/transactors/payment_channel/PayChan.h deleted file mode 100644 index cc23f029f2..0000000000 --- a/include/xrpl/tx/transactors/payment_channel/PayChan.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include - -namespace xrpl { - -class PayChanCreate : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; - - explicit PayChanCreate(ApplyContext& ctx) : Transactor(ctx) - { - } - - static TxConsequences - makeTxConsequences(PreflightContext const& ctx); - - static NotTEC - preflight(PreflightContext const& ctx); - - static TER - preclaim(PreclaimContext const& ctx); - - TER - doApply() override; -}; - -using PaymentChannelCreate = PayChanCreate; - -//------------------------------------------------------------------------------ - -class PayChanFund : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; - - explicit PayChanFund(ApplyContext& ctx) : Transactor(ctx) - { - } - - static TxConsequences - makeTxConsequences(PreflightContext const& ctx); - - static NotTEC - preflight(PreflightContext const& ctx); - - TER - doApply() override; -}; - -using PaymentChannelFund = PayChanFund; - -//------------------------------------------------------------------------------ - -class PayChanClaim : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - - explicit PayChanClaim(ApplyContext& ctx) : Transactor(ctx) - { - } - - static bool - checkExtraFeatures(PreflightContext const& ctx); - - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - - static NotTEC - preflight(PreflightContext const& ctx); - - static TER - preclaim(PreclaimContext const& ctx); - - TER - doApply() override; -}; - -using PaymentChannelClaim = PayChanClaim; - -} // namespace xrpl diff --git a/include/xrpl/tx/transactors/payment_channel/PaymentChannelClaim.h b/include/xrpl/tx/transactors/payment_channel/PaymentChannelClaim.h new file mode 100644 index 0000000000..449e503bc4 --- /dev/null +++ b/include/xrpl/tx/transactors/payment_channel/PaymentChannelClaim.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace xrpl { + +class PaymentChannelClaim : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit PaymentChannelClaim(ApplyContext& ctx) : Transactor(ctx) + { + } + + static bool + checkExtraFeatures(PreflightContext const& ctx); + + static std::uint32_t + getFlagsMask(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h b/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h new file mode 100644 index 0000000000..7fdacf9b3a --- /dev/null +++ b/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace xrpl { + +class PaymentChannelCreate : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; + + explicit PaymentChannelCreate(ApplyContext& ctx) : Transactor(ctx) + { + } + + static TxConsequences + makeTxConsequences(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/payment_channel/PaymentChannelFund.h b/include/xrpl/tx/transactors/payment_channel/PaymentChannelFund.h new file mode 100644 index 0000000000..69f74a4c53 --- /dev/null +++ b/include/xrpl/tx/transactors/payment_channel/PaymentChannelFund.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace xrpl { + +class PaymentChannelFund : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; + + explicit PaymentChannelFund(ApplyContext& ctx) : Transactor(ctx) + { + } + + static TxConsequences + makeTxConsequences(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/system/CreateTicket.h b/include/xrpl/tx/transactors/system/TicketCreate.h similarity index 91% rename from include/xrpl/tx/transactors/system/CreateTicket.h rename to include/xrpl/tx/transactors/system/TicketCreate.h index 867ad99c12..88d83c37a7 100644 --- a/include/xrpl/tx/transactors/system/CreateTicket.h +++ b/include/xrpl/tx/transactors/system/TicketCreate.h @@ -4,7 +4,7 @@ namespace xrpl { -class CreateTicket : public Transactor +class TicketCreate : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; @@ -25,7 +25,7 @@ public: // average, 1.25 ms. // // Using that same test set up creating 250 Tickets in a single - // CreateTicket::doApply() in a unit test took, on average, 1.21 ms. + // TicketCreate::doApply() in a unit test took, on average, 1.21 ms. // // So, for the moment, a single transaction creating 250 Tickets takes // about the same compute time as a single compute-intensive payment. @@ -41,7 +41,7 @@ public: // ledger-stuffing with Tickets. constexpr static std::uint32_t maxTicketThreshold = 250; - explicit CreateTicket(ApplyContext& ctx) : Transactor(ctx) + explicit TicketCreate(ApplyContext& ctx) : Transactor(ctx) { } @@ -61,6 +61,4 @@ public: doApply() override; }; -using TicketCreate = CreateTicket; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/token/MPTokenAuthorize.h b/include/xrpl/tx/transactors/token/MPTokenAuthorize.h index b6a34d4f14..3210608e73 100644 --- a/include/xrpl/tx/transactors/token/MPTokenAuthorize.h +++ b/include/xrpl/tx/transactors/token/MPTokenAuthorize.h @@ -31,13 +31,6 @@ public: static TER preclaim(PreclaimContext const& ctx); - static TER - createMPToken( - ApplyView& view, - MPTID const& mptIssuanceID, - AccountID const& account, - std::uint32_t const flags); - TER doApply() override; }; diff --git a/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h index 0ebde22a37..5ef12df282 100644 --- a/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h +++ b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h @@ -10,7 +10,7 @@ struct MPTCreateArgs { std::optional priorBalance; AccountID const& account; - std::uint32_t sequence; + std::uint32_t sequence = 0; std::uint32_t flags = 0; std::optional maxAmount{}; std::optional assetScale{}; diff --git a/include/xrpl/tx/transactors/token/SetTrust.h b/include/xrpl/tx/transactors/token/TrustSet.h similarity index 81% rename from include/xrpl/tx/transactors/token/SetTrust.h rename to include/xrpl/tx/transactors/token/TrustSet.h index 47ec26b6ad..2e67aaeded 100644 --- a/include/xrpl/tx/transactors/token/SetTrust.h +++ b/include/xrpl/tx/transactors/token/TrustSet.h @@ -5,12 +5,12 @@ namespace xrpl { -class SetTrust : public Transactor +class TrustSet : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit SetTrust(ApplyContext& ctx) : Transactor(ctx) + explicit TrustSet(ApplyContext& ctx) : Transactor(ctx) { } @@ -30,6 +30,4 @@ public: doApply() override; }; -using TrustSet = SetTrust; - } // namespace xrpl diff --git a/sanitizers/suppressions/asan.supp b/sanitizers/suppressions/asan.supp index 7ba98766bd..e02d580aa6 100644 --- a/sanitizers/suppressions/asan.supp +++ b/sanitizers/suppressions/asan.supp @@ -1,24 +1,6 @@ # The idea is to empty this file gradually by fixing the underlying issues and removing suppressions. # # ASAN_OPTIONS="print_stacktrace=1:detect_container_overflow=0:suppressions=sanitizers/suppressions/asan.supp:halt_on_error=0" -# -# The detect_container_overflow=0 option disables false positives from: -# - Boost intrusive containers (slist_iterator.hpp, hashtable.hpp, aged_unordered_container.h) -# - Boost context/coroutine stack switching (Workers.cpp, thread.h) -# -# See: https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow - -# Boost -interceptor_name:boost/asio - -# Leaks in Doctest tests: xrpl.test.* -interceptor_name:src/libxrpl/net/HTTPClient.cpp -interceptor_name:src/libxrpl/net/RegisterSSLCerts.cpp -interceptor_name:src/tests/libxrpl/net/HTTPClient.cpp -interceptor_name:xrpl/net/AutoSocket.h -interceptor_name:xrpl/net/HTTPClient.h -interceptor_name:xrpl/net/HTTPClientSSLContext.h -interceptor_name:xrpl/net/RegisterSSLCerts.h # Suppress false positive stack-buffer errors in thread stack allocation # Related to ASan's __asan_handle_no_return warnings (github.com/google/sanitizers/issues/189) diff --git a/sanitizers/suppressions/lsan.supp b/sanitizers/suppressions/lsan.supp index a81d7d89fa..30d01a2bfa 100644 --- a/sanitizers/suppressions/lsan.supp +++ b/sanitizers/suppressions/lsan.supp @@ -1,16 +1,5 @@ # The idea is to empty this file gradually by fixing the underlying issues and removing suppresions. -# Suppress leaks detected by asan in rippled code. -leak:src/libxrpl/net/HTTPClient.cpp -leak:src/libxrpl/net/RegisterSSLCerts.cpp -leak:src/tests/libxrpl/net/HTTPClient.cpp -leak:xrpl/net/AutoSocket.h -leak:xrpl/net/HTTPClient.h -leak:xrpl/net/HTTPClientSSLContext.h -leak:xrpl/net/RegisterSSLCerts.h -leak:ripple::HTTPClient -leak:ripple::HTTPClientImp - # Suppress leaks detected by asan in boost code. leak:boost::asio leak:boost/asio diff --git a/sanitizers/suppressions/runtime-asan-options.txt b/sanitizers/suppressions/runtime-asan-options.txt new file mode 100644 index 0000000000..fb2727f868 --- /dev/null +++ b/sanitizers/suppressions/runtime-asan-options.txt @@ -0,0 +1,8 @@ +detect_container_overflow=false +detect_stack_use_after_return=false +debug=true +halt_on_error=false +print_stats=true +print_cmdline=true +use_sigaltstack=0 +print_stacktrace=1 diff --git a/sanitizers/suppressions/runtime-lsan-options.txt b/sanitizers/suppressions/runtime-lsan-options.txt new file mode 100644 index 0000000000..fcfccf7bae --- /dev/null +++ b/sanitizers/suppressions/runtime-lsan-options.txt @@ -0,0 +1 @@ +halt_on_error=false diff --git a/sanitizers/suppressions/runtime-tsan-options.txt b/sanitizers/suppressions/runtime-tsan-options.txt new file mode 100644 index 0000000000..aafe997ea4 --- /dev/null +++ b/sanitizers/suppressions/runtime-tsan-options.txt @@ -0,0 +1,3 @@ +halt_on_error=false +verbosity=1 +second_deadlock_stack=1 diff --git a/sanitizers/suppressions/runtime-ubsan-options.txt b/sanitizers/suppressions/runtime-ubsan-options.txt new file mode 100644 index 0000000000..fcfccf7bae --- /dev/null +++ b/sanitizers/suppressions/runtime-ubsan-options.txt @@ -0,0 +1 @@ +halt_on_error=false diff --git a/sanitizers/suppressions/sanitizer-ignorelist.txt b/sanitizers/suppressions/sanitizer-ignorelist.txt index 5dbead48a2..dc9a31f8e4 100644 --- a/sanitizers/suppressions/sanitizer-ignorelist.txt +++ b/sanitizers/suppressions/sanitizer-ignorelist.txt @@ -27,3 +27,11 @@ src:core/JobQueue.cpp src:libxrpl/beast/utility/beast_Journal.cpp src:test/beast/beast_PropertyStream_test.cpp src:src/test/app/Invariants_test.cpp + +# ASan false positive: stack-use-after-scope in ErrorCodes.h inline functions. +# When Clang inlines the StaticString overloads (e.g. invalid_field_error(StaticString)), +# ASan scope-poisons the temporary std::string before the inlined callee finishes reading +# through the const ref. This corrupts the coroutine stack and crashes the Simulate test. +# See asan.supp comments for full explanation and planned fix. +[address] +src:*ErrorCodes.h diff --git a/sanitizers/suppressions/tsan.supp b/sanitizers/suppressions/tsan.supp index 74f3371e68..702eef1e3d 100644 --- a/sanitizers/suppressions/tsan.supp +++ b/sanitizers/suppressions/tsan.supp @@ -7,7 +7,7 @@ race:boost/context/ race:boost/asio/executor.hpp race:boost::asio -# Suppress tsan related issues in rippled code. +# Suppress tsan related issues in xrpld code. race:src/libxrpl/basics/make_SSLContext.cpp race:src/libxrpl/basics/Number.cpp race:src/libxrpl/json/json_value.cpp @@ -46,7 +46,7 @@ race:xrpl/server/detail/ServerImpl.h race:xrpl/nodestore/detail/DatabaseNodeImp.h race:src/libxrpl/beast/utility/beast_Journal.cpp race:src/test/beast/LexicalCast_test.cpp -race:ripple::ServerHandler +race:ServerHandler # More suppressions in external library code. race:crtstuff.c @@ -65,7 +65,7 @@ deadlock:src/xrpld/app/misc/detail/ValidatorSite.cpp signal:src/libxrpl/beast/utility/beast_Journal.cpp signal:src/xrpld/core/detail/Workers.cpp signal:src/xrpld/core/JobQueue.cpp -signal:ripple::Workers::Worker +signal:Workers::Worker # Aggressive suppressing of deadlock tsan errors deadlock:pthread_create diff --git a/sanitizers/suppressions/ubsan.supp b/sanitizers/suppressions/ubsan.supp index 1504ef685f..1e07065ebd 100644 --- a/sanitizers/suppressions/ubsan.supp +++ b/sanitizers/suppressions/ubsan.supp @@ -74,7 +74,7 @@ vptr:boost # Google protobuf undefined:protobuf -# Suppress UBSan errors in rippled code by source file path +# Suppress UBSan errors in xrpld code by source file path undefined:src/libxrpl/basics/base64.cpp undefined:src/libxrpl/basics/Number.cpp undefined:src/libxrpl/beast/utility/beast_Journal.cpp @@ -104,7 +104,7 @@ undefined:src/xrpld/app/misc/NetworkOPs.cpp undefined:src/libxrpl/json/json_value.cpp undefined:src/xrpld/app/paths/detail/StrandFlow.h undefined:src/xrpld/app/tx/detail/NFTokenMint.cpp -undefined:src/xrpld/app/tx/detail/SetOracle.cpp +undefined:src/xrpld/app/tx/detail/OracleSet.cpp undefined:src/xrpld/core/detail/JobQueue.cpp undefined:src/xrpld/core/detail/Workers.cpp undefined:src/xrpld/rpc/detail/Role.cpp @@ -154,7 +154,7 @@ unsigned-integer-overflow:src/xrpld/app/misc/detail/AmendmentTable.cpp unsigned-integer-overflow:src/xrpld/app/misc/NetworkOPs.cpp unsigned-integer-overflow:src/xrpld/app/paths/detail/StrandFlow.h unsigned-integer-overflow:src/xrpld/app/tx/detail/NFTokenMint.cpp -unsigned-integer-overflow:src/xrpld/app/tx/detail/SetOracle.cpp +unsigned-integer-overflow:src/xrpld/app/tx/detail/OracleSet.cpp unsigned-integer-overflow:src/xrpld/rpc/detail/Role.cpp unsigned-integer-overflow:src/xrpld/rpc/handlers/GetAggregatePrice.cpp unsigned-integer-overflow:xrpl/basics/base_uint.h @@ -165,7 +165,7 @@ unsigned-integer-overflow:xrpl/nodestore/detail/varint.h unsigned-integer-overflow:xrpl/peerfinder/detail/Counts.h unsigned-integer-overflow:xrpl/protocol/nft.h -# Rippled intentional overflows and operations +# Xrpld intentional overflows and operations # STAmount uses intentional negation of INT64_MIN and overflow in arithmetic signed-integer-overflow:src/libxrpl/protocol/STAmount.cpp unsigned-integer-overflow:src/libxrpl/protocol/STAmount.cpp @@ -182,6 +182,17 @@ signed-integer-overflow:src/test/beast/LexicalCast_test.cpp # External library suppressions unsigned-integer-overflow:nudb/detail/xxhash.hpp +# Loan_test.cpp intentional underflow in test arithmetic +unsigned-integer-overflow:src/test/app/Loan_test.cpp +undefined:src/test/app/Loan_test.cpp + +# Source tree restructured paths (libxrpl/tx/transactors/) +# These duplicate the xrpld/app/tx/detail entries above for the new layout +unsigned-integer-overflow:src/libxrpl/tx/transactors/oracle/OracleSet.cpp +undefined:src/libxrpl/tx/transactors/oracle/OracleSet.cpp +unsigned-integer-overflow:src/libxrpl/tx/transactors/nft/NFTokenMint.cpp +undefined:src/libxrpl/tx/transactors/nft/NFTokenMint.cpp + # Protobuf intentional overflows in hash functions # Protobuf uses intentional unsigned overflow for hash computation (stringpiece.h:393) unsigned-integer-overflow:google/protobuf/stubs/stringpiece.h diff --git a/scripts/generate_ledger_classes.py b/scripts/generate_ledger_classes.py new file mode 100644 index 0000000000..ad773ab9af --- /dev/null +++ b/scripts/generate_ledger_classes.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 +""" +Generate C++ wrapper classes for XRP Ledger entry types from ledger_entries.macro. + +This script parses the ledger_entries.macro file and generates type-safe wrapper +classes for each ledger entry type, similar to the transaction wrapper classes. + +Uses pcpp to preprocess the macro file and pyparsing to parse the DSL. +""" + +import io +import argparse +from pathlib import Path +import pyparsing as pp + +# Import common utilities +from macro_parser_common import ( + CppCleaner, + parse_sfields_macro, + parse_field_list, + generate_cpp_class, + generate_from_template, + clear_output_directory, +) + + +def create_ledger_entry_parser(): + """Create a pyparsing parser for LEDGER_ENTRY macros. + + This parser extracts the full LEDGER_ENTRY macro call and parses its arguments + using pyparsing's nesting-aware delimited list parsing. + """ + # Match the exact words + ledger_entry = pp.Keyword("LEDGER_ENTRY") | pp.Keyword("LEDGER_ENTRY_DUPLICATE") + + # Define nested structures so pyparsing protects them + nested_braces = pp.original_text_for(pp.nested_expr("{", "}")) + nested_parens = pp.original_text_for(pp.nested_expr("(", ")")) + + # Define standard text (anything that isn't a comma, parens, or braces) + plain_text = pp.Word(pp.printables + " \t\n", exclude_chars=",{}()") + + # A single argument is any combination of the above + single_arg = pp.Combine(pp.OneOrMore(nested_braces | nested_parens | plain_text)) + single_arg.set_parse_action(lambda t: t[0].strip()) + + # The arguments are a delimited list + args_list = pp.DelimitedList(single_arg) + + # The full macro: LEDGER_ENTRY(args) or LEDGER_ENTRY_DUPLICATE(args) + macro_parser = ( + ledger_entry + pp.Suppress("(") + pp.Group(args_list)("args") + pp.Suppress(")") + ) + + return macro_parser + + +def parse_ledger_entry_args(args_list): + """Parse the arguments of a LEDGER_ENTRY macro call. + + Args: + args_list: A list of parsed arguments from pyparsing, e.g., + ['ltACCOUNT_ROOT', '0x0061', 'AccountRoot', 'account', '({...})'] + + Returns: + A dict with parsed ledger entry information. + """ + if len(args_list) < 5: + raise ValueError( + f"Expected at least 5 parts in LEDGER_ENTRY, got {len(args_list)}: {args_list}" + ) + + tag = args_list[0] + value = args_list[1] + name = args_list[2] + rpc_name = args_list[3] + fields_str = args_list[-1] + + # Parse fields: ({field1, field2, ...}) + fields = parse_field_list(fields_str) + + return { + "tag": tag, + "value": value, + "name": name, + "rpc_name": rpc_name, + "fields": fields, + } + + +def parse_macro_file(file_path): + """Parse the ledger_entries.macro file and return a list of ledger entry definitions. + + Uses pcpp to preprocess the file and pyparsing to parse the LEDGER_ENTRY macros. + """ + with open(file_path, "r") as f: + c_code = f.read() + + # Step 1: Clean the C++ code using pcpp + cleaner = CppCleaner("LEDGER_ENTRY_INCLUDE", "LEDGER_ENTRY") + cleaner.parse(c_code) + + out = io.StringIO() + cleaner.write(out) + clean_text = out.getvalue() + + # Step 2: Parse the clean text using pyparsing + parser = create_ledger_entry_parser() + entries = [] + + for match, _, _ in parser.scan_string(clean_text): + # Extract the macro name and arguments + raw_args = match.args + + # Parse the arguments + entry_data = parse_ledger_entry_args(raw_args) + entries.append(entry_data) + + return entries + + +def main(): + parser = argparse.ArgumentParser( + description="Generate C++ ledger entry classes from ledger_entries.macro" + ) + parser.add_argument("macro_path", help="Path to ledger_entries.macro") + parser.add_argument( + "--header-dir", + help="Output directory for header files", + default="include/xrpl/protocol_autogen/ledger_entries", + ) + parser.add_argument( + "--test-dir", + help="Output directory for test files (optional)", + default=None, + ) + parser.add_argument( + "--sfields-macro", + help="Path to sfields.macro (default: auto-detect from macro_path)", + ) + parser.add_argument( + "--list-outputs", + action="store_true", + help="List output files without generating (one per line)", + ) + + args = parser.parse_args() + + # Parse the macro file to get ledger entry names + entries = parse_macro_file(args.macro_path) + + # If --list-outputs, just print the output file paths and exit + if args.list_outputs: + header_dir = Path(args.header_dir) + for entry in entries: + print(header_dir / f"{entry['name']}.h") + if args.test_dir: + test_dir = Path(args.test_dir) + for entry in entries: + print(test_dir / f"{entry['name']}Tests.cpp") + return + + # Auto-detect sfields.macro path if not provided + if args.sfields_macro: + sfields_path = Path(args.sfields_macro) + else: + # Assume sfields.macro is in the same directory as ledger_entries.macro + macro_path = Path(args.macro_path) + sfields_path = macro_path.parent / "sfields.macro" + + # Parse sfields.macro to get field type information + print(f"Parsing {sfields_path}...") + field_types = parse_sfields_macro(sfields_path) + print( + f"Found {len(field_types)} field definitions ({sum(1 for f in field_types.values() if f['typed'])} typed, {sum(1 for f in field_types.values() if not f['typed'])} untyped)\n" + ) + + print(f"Found {len(entries)} ledger entries\n") + + for entry in entries: + print(f"Ledger Entry: {entry['name']}") + print(f" Tag: {entry['tag']}") + print(f" Value: {entry['value']}") + print(f" RPC Name: {entry['rpc_name']}") + print(f" Fields: {len(entry['fields'])}") + for field in entry["fields"]: + mpt_info = f" ({field['mpt_support']})" if "mpt_support" in field else "" + print(f" - {field['name']}: {field['requirement']}{mpt_info}") + print() + + # Set up template directory + script_dir = Path(__file__).parent + template_dir = script_dir / "templates" + + # Generate C++ classes + header_dir = Path(args.header_dir) + header_dir.mkdir(parents=True, exist_ok=True) + + # Clear existing generated files before regenerating + clear_output_directory(header_dir) + + for entry in entries: + generate_cpp_class( + entry, header_dir, template_dir, field_types, "LedgerEntry.h.mako" + ) + + print(f"\nGenerated {len(entries)} ledger entry classes") + + # Generate unit tests if --test-dir is provided + if args.test_dir: + test_dir = Path(args.test_dir) + test_dir.mkdir(parents=True, exist_ok=True) + + # Clear existing generated test files before regenerating + clear_output_directory(test_dir) + + for entry in entries: + # Fields are already enriched from generate_cpp_class above + generate_from_template( + entry, test_dir, template_dir, "LedgerEntryTests.cpp.mako", "Tests.cpp" + ) + + print(f"\nGenerated {len(entries)} ledger entry test files") + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_tx_classes.py b/scripts/generate_tx_classes.py new file mode 100644 index 0000000000..f21b7101df --- /dev/null +++ b/scripts/generate_tx_classes.py @@ -0,0 +1,247 @@ +#!/usr/bin/env python3 +""" +Parse transactions.macro file to extract transaction information +and generate C++ classes for each transaction type. + +Uses pcpp to preprocess the macro file and pyparsing to parse the DSL. +""" + +import io +import argparse +from pathlib import Path +import pyparsing as pp + +# Import common utilities +from macro_parser_common import ( + CppCleaner, + parse_sfields_macro, + parse_field_list, + generate_cpp_class, + generate_from_template, + clear_output_directory, +) + + +def create_transaction_parser(): + """Create a pyparsing parser for TRANSACTION macros. + + This parser extracts the full TRANSACTION macro call and parses its arguments + using pyparsing's nesting-aware delimited list parsing. + """ + # Define nested structures so pyparsing protects them + nested_braces = pp.original_text_for(pp.nested_expr("{", "}")) + nested_parens = pp.original_text_for(pp.nested_expr("(", ")")) + + # Define standard text (anything that isn't a comma, parens, or braces) + plain_text = pp.Word(pp.printables + " \t\n", exclude_chars=",{}()") + + # A single argument is any combination of the above + single_arg = pp.Combine(pp.OneOrMore(nested_braces | nested_parens | plain_text)) + single_arg.set_parse_action(lambda t: t[0].strip()) + + # The arguments are a delimited list + args_list = pp.DelimitedList(single_arg) + + # The full macro: TRANSACTION(args) + macro_parser = ( + pp.Keyword("TRANSACTION") + + pp.Suppress("(") + + pp.Group(args_list)("args") + + pp.Suppress(")") + ) + + return macro_parser + + +def parse_transaction_args(args_list): + """Parse the arguments of a TRANSACTION macro call. + + Args: + args_list: A list of parsed arguments from pyparsing, e.g., + ['ttPAYMENT', '0', 'Payment', 'Delegation::delegable', + 'uint256{}', 'createAcct', '({...})'] + + Returns: + A dict with parsed transaction information. + """ + if len(args_list) < 7: + raise ValueError( + f"Expected at least 7 parts in TRANSACTION, got {len(args_list)}: {args_list}" + ) + + tag = args_list[0] + value = args_list[1] + name = args_list[2] + delegable = args_list[3] + amendments = args_list[4] + privileges = args_list[5] + fields_str = args_list[-1] + + # Parse fields: ({field1, field2, ...}) + fields = parse_field_list(fields_str) + + return { + "tag": tag, + "value": value, + "name": name, + "delegable": delegable, + "amendments": amendments, + "privileges": privileges, + "fields": fields, + } + + +def parse_macro_file(filepath): + """Parse the transactions.macro file. + + Uses pcpp to preprocess the file and pyparsing to parse the TRANSACTION macros. + """ + with open(filepath, "r") as f: + c_code = f.read() + + # Step 1: Clean the C++ code using pcpp + cleaner = CppCleaner("TRANSACTION_INCLUDE", "TRANSACTION") + cleaner.parse(c_code) + + out = io.StringIO() + cleaner.write(out) + clean_text = out.getvalue() + + # Step 2: Parse the clean text using pyparsing + parser = create_transaction_parser() + transactions = [] + + for match, _, _ in parser.scan_string(clean_text): + # Extract the macro name and arguments + raw_args = match.args + + # Parse the arguments + tx_data = parse_transaction_args(raw_args) + transactions.append(tx_data) + + return transactions + + +# TransactionBase is a static file in the repository at: +# - include/xrpl/protocol/TransactionBase.h +# - src/libxrpl/protocol/TransactionBase.cpp +# It is NOT generated by this script. + + +def main(): + parser = argparse.ArgumentParser( + description="Generate C++ transaction classes from transactions.macro" + ) + parser.add_argument("macro_path", help="Path to transactions.macro") + parser.add_argument( + "--header-dir", + help="Output directory for header files", + default="include/xrpl/protocol_autogen/transactions", + ) + parser.add_argument( + "--test-dir", + help="Output directory for test files (optional)", + default=None, + ) + parser.add_argument( + "--sfields-macro", + help="Path to sfields.macro (default: auto-detect from macro_path)", + ) + parser.add_argument( + "--list-outputs", + action="store_true", + help="List output files without generating (one per line)", + ) + + args = parser.parse_args() + + # Parse the macro file to get transaction names + transactions = parse_macro_file(args.macro_path) + + # If --list-outputs, just print the output file paths and exit + if args.list_outputs: + header_dir = Path(args.header_dir) + for tx in transactions: + print(header_dir / f"{tx['name']}.h") + if args.test_dir: + test_dir = Path(args.test_dir) + for tx in transactions: + print(test_dir / f"{tx['name']}Tests.cpp") + return + + # Auto-detect sfields.macro path if not provided + if args.sfields_macro: + sfields_path = Path(args.sfields_macro) + else: + # Assume sfields.macro is in the same directory as transactions.macro + macro_path = Path(args.macro_path) + sfields_path = macro_path.parent / "sfields.macro" + + # Parse sfields.macro to get field type information + print(f"Parsing {sfields_path}...") + field_types = parse_sfields_macro(sfields_path) + print( + f"Found {len(field_types)} field definitions ({sum(1 for f in field_types.values() if f['typed'])} typed, {sum(1 for f in field_types.values() if not f['typed'])} untyped)\n" + ) + + print(f"Found {len(transactions)} transactions\n") + + for tx in transactions: + print(f"Transaction: {tx['name']}") + print(f" Tag: {tx['tag']}") + print(f" Value: {tx['value']}") + print(f" Fields: {len(tx['fields'])}") + for field in tx["fields"]: + print(f" - {field['name']}: {field['requirement']}") + print() + + # Set up output directory + header_dir = Path(args.header_dir) + header_dir.mkdir(parents=True, exist_ok=True) + + # Clear existing generated files before regenerating + clear_output_directory(header_dir) + + print(f"\nGenerating header-only template classes...") + print(f" Headers: {header_dir}\n") + + # Set up template directory + script_dir = Path(__file__).parent + template_dir = script_dir / "templates" + + generated_files = [] + for tx_info in transactions: + header_path = generate_cpp_class( + tx_info, header_dir, template_dir, field_types, "Transaction.h.mako" + ) + generated_files.append(header_path) + print(f" Generated: {tx_info['name']}.h") + + print( + f"\nGenerated {len(transactions)} transaction classes ({len(generated_files)} header files)" + ) + print(f" Headers: {header_dir.absolute()}") + + # Generate unit tests if --test-dir is provided + if args.test_dir: + test_dir = Path(args.test_dir) + test_dir.mkdir(parents=True, exist_ok=True) + + # Clear existing generated test files before regenerating + clear_output_directory(test_dir) + + for tx_info in transactions: + # Fields are already enriched from generate_cpp_class above + generate_from_template( + tx_info, + test_dir, + template_dir, + "TransactionTests.cpp.mako", + "Tests.cpp", + ) + + print(f"\nGenerated {len(transactions)} transaction test files") + + +if __name__ == "__main__": + main() diff --git a/scripts/macro_parser_common.py b/scripts/macro_parser_common.py new file mode 100644 index 0000000000..eae4df39db --- /dev/null +++ b/scripts/macro_parser_common.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 +""" +Common utilities for parsing XRP Ledger macro files. + +This module provides shared functionality for parsing transactions.macro +and ledger_entries.macro files using pcpp and pyparsing. +""" + +import re +import shutil +from pathlib import Path +import pyparsing as pp +from pcpp import Preprocessor + + +def clear_output_directory(directory): + """Clear all generated files from an output directory. + + Removes all .h and .cpp files from the directory, but preserves + the directory itself and any subdirectories. + + Args: + directory: Path to the directory to clear + """ + dir_path = Path(directory) + if not dir_path.exists(): + return + + # Remove generated files (headers and source files) + for pattern in ["*.h", "*.cpp"]: + for file_path in dir_path.glob(pattern): + file_path.unlink() + + print(f"Cleared output directory: {dir_path}") + + +class CppCleaner(Preprocessor): + """C preprocessor that removes C++ noise while preserving macro calls.""" + + def __init__(self, macro_include_name, macro_name): + """ + Initialize the preprocessor. + + Args: + macro_include_name: The name of the include flag to set to 0 + (e.g., "TRANSACTION_INCLUDE" or "LEDGER_ENTRY_INCLUDE") + macro_name: The name of the macro to define so #if !defined() checks pass + (e.g., "TRANSACTION" or "LEDGER_ENTRY") + """ + super(CppCleaner, self).__init__() + # Define flags so #if blocks evaluate correctly + # We set the include flag to 0 so includes are skipped + self.define(f"{macro_include_name} 0") + # Define the macro so #if !defined(MACRO) / #error checks pass + # We define it to expand to itself so the macro calls remain in the output + # for pyparsing to find and parse + self.define(f"{macro_name}(...) {macro_name}(__VA_ARGS__)") + # Suppress line directives + self.line_directive = None + + def on_error(self, file, line, msg): + # Ignore #error directives + pass + + def on_include_not_found( + self, is_malformed, is_system_include, curdir, includepath + ): + # Ignore missing headers + pass + + +def parse_sfields_macro(sfields_path): + """ + Parse sfields.macro to determine which fields are typed vs untyped. + + Returns a dict mapping field names to their type information: + { + 'sfMemos': {'typed': False, 'stiSuffix': 'ARRAY', 'typeData': {...}}, + 'sfAmount': {'typed': True, 'stiSuffix': 'AMOUNT', 'typeData': {...}}, + ... + } + """ + # Mapping from STI suffix to C++ type for untyped fields + UNTYPED_TYPE_MAP = { + "ARRAY": { + "getter_method": "getFieldArray", + "setter_method": "setFieldArray", + "setter_use_brackets": False, + "setter_type": "STArray const&", + "return_type": "STArray const&", + "return_type_optional": "std::optional>", + }, + "OBJECT": { + "getter_method": "getFieldObject", + "setter_method": "setFieldObject", + "setter_use_brackets": False, + "setter_type": "STObject const&", + "return_type": "STObject", + "return_type_optional": "std::optional", + }, + "PATHSET": { + "getter_method": "getFieldPathSet", + "setter_method": "setFieldPathSet", + "setter_use_brackets": False, + "setter_type": "STPathSet const&", + "return_type": "STPathSet const&", + "return_type_optional": "std::optional>", + }, + } + + field_info = {} + + with open(sfields_path, "r") as f: + content = f.read() + + # Parse TYPED_SFIELD entries + # Format: TYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) + typed_pattern = r"TYPED_SFIELD\s*\(\s*(\w+)\s*,\s*(\w+)\s*," + for match in re.finditer(typed_pattern, content): + field_name = match.group(1) + sti_suffix = match.group(2) + field_info[field_name] = { + "typed": True, + "stiSuffix": sti_suffix, + "typeData": { + "getter_method": "at", + "setter_method": "", + "setter_use_brackets": True, + "setter_type": f"std::decay_t const&", + "return_type": f"SF_{sti_suffix}::type::value_type", + "return_type_optional": f"protocol_autogen::Optional", + }, + } + + # Parse UNTYPED_SFIELD entries + # Format: UNTYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) + untyped_pattern = r"UNTYPED_SFIELD\s*\(\s*(\w+)\s*,\s*(\w+)\s*," + for match in re.finditer(untyped_pattern, content): + field_name = match.group(1) + sti_suffix = match.group(2) + type_data = UNTYPED_TYPE_MAP.get( + sti_suffix, UNTYPED_TYPE_MAP.get("OBJECT") + ) # Default to OBJECT + field_info[field_name] = { + "typed": False, + "stiSuffix": sti_suffix, + "typeData": type_data, + } + + return field_info + + +def create_field_list_parser(): + """Create a pyparsing parser for field lists like '({...})'.""" + # A field identifier (e.g., sfDestination, soeREQUIRED, soeMPTSupported) + field_identifier = pp.Word(pp.alphas + "_", pp.alphanums + "_") + + # A single field definition: {sfName, soeREQUIRED, ...} + # Allow optional trailing comma inside the braces + field_def = ( + pp.Suppress("{") + + pp.Group(pp.DelimitedList(field_identifier) + pp.Optional(pp.Suppress(",")))( + "parts" + ) + + pp.Suppress("}") + ) + + # The field list: ({field1, field2, ...}) or ({}) for empty lists + # Allow optional trailing comma after the last field definition + field_list = ( + pp.Suppress("(") + + pp.Suppress("{") + + pp.Group( + pp.Optional(pp.DelimitedList(field_def) + pp.Optional(pp.Suppress(","))) + )("fields") + + pp.Suppress("}") + + pp.Suppress(")") + ) + + return field_list + + +def parse_field_list(fields_str): + """Parse a field list string like '({...})' using pyparsing. + + Args: + fields_str: A string like '({ + {sfDestination, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTSupported} + })' + + Returns: + A list of field dicts with 'name', 'requirement', 'flags', and 'supports_mpt'. + """ + parser = create_field_list_parser() + + try: + result = parser.parse_string(fields_str, parse_all=True) + fields = [] + + for field_parts in result.fields: + if len(field_parts) < 2: + continue + + field_name = field_parts[0] + requirement = field_parts[1] + flags = list(field_parts[2:]) if len(field_parts) > 2 else [] + supports_mpt = "soeMPTSupported" in flags + + fields.append( + { + "name": field_name, + "requirement": requirement, + "flags": flags, + "supports_mpt": supports_mpt, + } + ) + + return fields + except pp.ParseException as e: + raise ValueError(f"Failed to parse field list: {e}") + + +def enrich_fields_with_type_data(entry_info, field_types): + """Enrich field information with type data from sfields.macro. + + Args: + entry_info: Dict containing entry information (name, fields, etc.) + field_types: Dict mapping field names to type information + + Modifies entry_info["fields"] in place. + """ + for field in entry_info["fields"]: + field_name = field["name"] + if field_name in field_types: + field["typed"] = field_types[field_name]["typed"] + field["paramName"] = field_name[2].lower() + field_name[3:] + field["stiSuffix"] = field_types[field_name]["stiSuffix"] + field["typeData"] = field_types[field_name]["typeData"] + else: + # Unknown field - assume typed for safety + field["typed"] = True + field["paramName"] = "" + field["stiSuffix"] = None + field["typeData"] = None + + +def generate_from_template( + entry_info, output_dir, template_dir, template_name, output_suffix +): + """Generate a file from a Mako template. + + Args: + entry_info: Dict containing entry information (name, fields, etc.) + Fields should already be enriched with type data. + output_dir: Output directory for generated files + template_dir: Directory containing Mako templates + template_name: Name of the Mako template file to use + output_suffix: Suffix for the output file (e.g., ".h" or "Tests.cpp") + + Returns: + Path to the generated file + """ + from mako.template import Template + + template_path = Path(template_dir) / template_name + template = Template(filename=str(template_path)) + + # Render the template - pass entry_info directly so templates can access any field + content = template.render(**entry_info) + + # Write output file in binary mode to avoid any line ending conversion + output_path = Path(output_dir) / f"{entry_info['name']}{output_suffix}" + with open(output_path, "wb") as f: + f.write(content.encode("utf-8")) + + print(f"Generated {output_path}") + return output_path + + +def generate_cpp_class( + entry_info, header_dir, template_dir, field_types, template_name +): + """Generate C++ header file from a Mako template. + + Args: + entry_info: Dict containing entry information (name, fields, etc.) + header_dir: Output directory for generated header files + template_dir: Directory containing Mako templates + field_types: Dict mapping field names to type information + template_name: Name of the Mako template file to use + """ + # Enrich field information with type data + enrich_fields_with_type_data(entry_info, field_types) + + # Generate the header file + generate_from_template(entry_info, header_dir, template_dir, template_name, ".h") diff --git a/scripts/requirements.txt b/scripts/requirements.txt new file mode 100644 index 0000000000..40b472078d --- /dev/null +++ b/scripts/requirements.txt @@ -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.0 diff --git a/scripts/templates/LedgerEntry.h.mako b/scripts/templates/LedgerEntry.h.mako new file mode 100644 index 0000000000..fdb55a973a --- /dev/null +++ b/scripts/templates/LedgerEntry.h.mako @@ -0,0 +1,216 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class ${name}Builder; + +/** + * @brief Ledger Entry: ${name} + * + * Type: ${tag} (${value}) + * RPC Name: ${rpc_name} + * + * Immutable wrapper around SLE providing type-safe field access. + * Use ${name}Builder to construct new ledger entries. + */ +class ${name} : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ${tag}; + + /** + * @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) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for ${name}"); + } + } + + // Ledger entry-specific field getters +% for field in fields: +% if field['typed']: + + /** + * @brief Get ${field['name']} (${field['requirement']}) +% if field.get('mpt_support'): + * MPT Support: ${field['mpt_support']} +% endif +% if field['requirement'] == 'soeREQUIRED': + * @return The field value. +% else: + * @return The field value, or std::nullopt if not present. +% endif + */ +% if field['requirement'] == 'soeREQUIRED': + [[nodiscard]] + ${field['typeData']['return_type']} + get${field['name'][2:]}() const + { + return this->sle_->${field['typeData']['getter_method']}(${field['name']}); + } +% else: + [[nodiscard]] + ${field['typeData']['return_type_optional']} + get${field['name'][2:]}() const + { + if (has${field['name'][2:]}()) + return this->sle_->${field['typeData']['getter_method']}(${field['name']}); + return std::nullopt; + } + + /** + * @brief Check if ${field['name']} is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + has${field['name'][2:]}() const + { + return this->sle_->isFieldPresent(${field['name']}); + } +% endif +% else: + + /** + * @brief Get ${field['name']} (${field['requirement']}) +% if field.get('mpt_support'): + * MPT Support: ${field['mpt_support']} +% endif + * @note This is an untyped field (${field.get('cppType', 'unknown')}). +% if field['requirement'] == 'soeREQUIRED': + * @return The field value. +% else: + * @return The field value, or std::nullopt if not present. +% endif + */ +% if field['requirement'] == 'soeREQUIRED': + [[nodiscard]] + ${field['typeData']['return_type']} + get${field['name'][2:]}() const + { + return this->sle_->${field['typeData']['getter_method']}(${field['name']}); + } +% else: + [[nodiscard]] + ${field['typeData']['return_type_optional']} + get${field['name'][2:]}() const + { + if (this->sle_->isFieldPresent(${field['name']})) + return this->sle_->${field['typeData']['getter_method']}(${field['name']}); + return std::nullopt; + } + + /** + * @brief Check if ${field['name']} is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + has${field['name'][2:]}() const + { + return this->sle_->isFieldPresent(${field['name']}); + } +% endif +% endif +% endfor +}; + +<% + required_fields = [f for f in fields if f['requirement'] == 'soeREQUIRED'] +%>\ +/** + * @brief Builder for ${name} ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class ${name}Builder : public LedgerEntryBuilderBase<${name}Builder> +{ +public: + /** + * @brief Construct a new ${name}Builder with required fields. +% for field in required_fields: + * @param ${field['paramName']} The ${field['name']} field value. +% endfor + */ + ${name}Builder(\ +% for i, field in enumerate(required_fields): +${field['typeData']['setter_type']} ${field['paramName']}${',' if i < len(required_fields) - 1 else ''}\ +% endfor +) + : LedgerEntryBuilderBase<${name}Builder>(${tag}) + { +% for field in required_fields: + set${field['name'][2:]}(${field['paramName']}); +% endfor + } + + /** + * @brief Construct a ${name}Builder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + ${name}Builder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ${tag}) + { + throw std::runtime_error("Invalid ledger entry type for ${name}"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ +% for field in fields: + + /** + * @brief Set ${field['name']} (${field['requirement']}) +% if field.get('mpt_support'): + * MPT Support: ${field['mpt_support']} +% endif + * @return Reference to this builder for method chaining. + */ + ${name}Builder& + set${field['name'][2:]}(${field['typeData']['setter_type']} value) + { +% if field.get('stiSuffix') == 'ISSUE': + object_[${field['name']}] = STIssue(${field['name']}, value); +% elif field['typeData'].get('setter_use_brackets'): + object_[${field['name']}] = value; +% else: + object_.${field['typeData']['setter_method']}(${field['name']}, value); +% endif + return *this; + } +% endfor + + /** + * @brief Build and return the completed ${name} wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + ${name} + build(uint256 const& index) + { + return ${name}{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/scripts/templates/LedgerEntryTests.cpp.mako b/scripts/templates/LedgerEntryTests.cpp.mako new file mode 100644 index 0000000000..35ce57f17b --- /dev/null +++ b/scripts/templates/LedgerEntryTests.cpp.mako @@ -0,0 +1,231 @@ +// Auto-generated unit tests for ledger entry ${name} +<% + required_fields = [f for f in fields if f["requirement"] == "soeREQUIRED"] + optional_fields = [f for f in fields if f["requirement"] != "soeREQUIRED"] + + def canonical_expr(field): + return f"canonical_{field['stiSuffix']}()" + + # Pick a wrong ledger entry to test type mismatch + # Use Ticket as it has minimal required fields (just Account) + if name != "Ticket": + wrong_le_include = "Ticket" + else: + wrong_le_include = "Check" +%> + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(${name}Tests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + +% for field in fields: + auto const ${field["paramName"]}Value = ${canonical_expr(field)}; +% endfor + + ${name}Builder builder{ +% for i, field in enumerate(required_fields): + ${field["paramName"]}Value${"," if i < len(required_fields) - 1 else ""} +% endfor + }; + +% for field in optional_fields: + builder.set${field["name"][2:]}(${field["paramName"]}Value); +% endfor + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + +% for field in required_fields: + { + auto const& expected = ${field["paramName"]}Value; + auto const actual = entry.get${field["name"][2:]}(); + expectEqualField(expected, actual, "${field["name"]}"); + } + +% endfor +% for field in optional_fields: + { + auto const& expected = ${field["paramName"]}Value; + auto const actualOpt = entry.get${field["name"][2:]}(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "${field["name"]}"); + EXPECT_TRUE(entry.has${field["name"][2:]}()); + } + +% endfor + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(${name}Tests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + +% for field in fields: + auto const ${field["paramName"]}Value = ${canonical_expr(field)}; +% endfor + + auto sle = std::make_shared(${name}::entryType, index); + +% for field in fields: +% if field.get("stiSuffix") == "ISSUE": + sle->at(${field["name"]}) = STIssue(${field["name"]}, ${field["paramName"]}Value); +% elif field["typeData"].get("setter_use_brackets"): + sle->at(${field["name"]}) = ${field["paramName"]}Value; +% else: + sle->${field["typeData"]["setter_method"]}(${field["name"]}, ${field["paramName"]}Value); +% endif +% endfor + + ${name}Builder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + ${name} entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + +% for field in required_fields: + { + auto const& expected = ${field["paramName"]}Value; + + auto const fromSle = entryFromSle.get${field["name"][2:]}(); + auto const fromBuilder = entryFromBuilder.get${field["name"][2:]}(); + + expectEqualField(expected, fromSle, "${field["name"]}"); + expectEqualField(expected, fromBuilder, "${field["name"]}"); + } + +% endfor +% for field in optional_fields: + { + auto const& expected = ${field["paramName"]}Value; + + auto const fromSleOpt = entryFromSle.get${field["name"][2:]}(); + auto const fromBuilderOpt = entryFromBuilder.get${field["name"][2:]}(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "${field["name"]}"); + expectEqualField(expected, *fromBuilderOpt, "${field["name"]}"); + } + +% endfor + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(${name}Tests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq +% if wrong_le_include == "Ticket": + ${wrong_le_include}Builder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; +% else: + ${wrong_le_include}Builder wrongBuilder{ + canonical_ACCOUNT(), + canonical_ACCOUNT(), + canonical_AMOUNT(), + canonical_UINT32(), + canonical_UINT64(), + canonical_UINT64(), + canonical_UINT256(), + canonical_UINT32()}; +% endif + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(${name}{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(${name}Tests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type +% if wrong_le_include == "Ticket": + ${wrong_le_include}Builder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; +% else: + ${wrong_le_include}Builder wrongBuilder{ + canonical_ACCOUNT(), + canonical_ACCOUNT(), + canonical_AMOUNT(), + canonical_UINT32(), + canonical_UINT64(), + canonical_UINT64(), + canonical_UINT256(), + canonical_UINT32()}; +% endif + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(${name}Builder{wrongEntry.getSle()}, std::runtime_error); +} + +% if optional_fields: +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(${name}Tests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + +% for field in required_fields: + auto const ${field["paramName"]}Value = ${canonical_expr(field)}; +% endfor + + ${name}Builder builder{ +% for i, field in enumerate(required_fields): + ${field["paramName"]}Value${"," if i < len(required_fields) - 1 else ""} +% endfor + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present +% for field in optional_fields: + EXPECT_FALSE(entry.has${field["name"][2:]}()); + EXPECT_FALSE(entry.get${field["name"][2:]}().has_value()); +% endfor +} +% endif +} diff --git a/scripts/templates/Transaction.h.mako b/scripts/templates/Transaction.h.mako new file mode 100644 index 0000000000..62c51a5c97 --- /dev/null +++ b/scripts/templates/Transaction.h.mako @@ -0,0 +1,226 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class ${name}Builder; + +/** + * @brief Transaction: ${name} + * + * Type: ${tag} (${value}) + * Delegable: ${delegable} + * Amendment: ${amendments} + * Privileges: ${privileges} + * + * Immutable wrapper around STTx providing type-safe field access. + * Use ${name}Builder to construct new transactions. + */ +class ${name} : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ${tag}; + + /** + * @brief Construct a ${name} transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit ${name}(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for ${name}"); + } + } + + // Transaction-specific field getters +% for field in fields: +% if field['typed']: + + /** + * @brief Get ${field['name']} (${field['requirement']}) +% if field.get('supports_mpt'): + * @note This field supports MPT (Multi-Purpose Token) amounts. +% endif +% if field['requirement'] == 'soeREQUIRED': + * @return The field value. +% else: + * @return The field value, or std::nullopt if not present. +% endif + */ +% if field['requirement'] == 'soeREQUIRED': + [[nodiscard]] + ${field['typeData']['return_type']} + get${field['name'][2:]}() const + { + return this->tx_->${field['typeData']['getter_method']}(${field['name']}); + } +% else: + [[nodiscard]] + ${field['typeData']['return_type_optional']} + get${field['name'][2:]}() const + { + if (has${field['name'][2:]}()) + { + return this->tx_->${field['typeData']['getter_method']}(${field['name']}); + } + return std::nullopt; + } + + /** + * @brief Check if ${field['name']} is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + has${field['name'][2:]}() const + { + return this->tx_->isFieldPresent(${field['name']}); + } +% endif +% else: + /** + * @brief Get ${field['name']} (${field['requirement']}) +% if field.get('supports_mpt'): + * @note This field supports MPT (Multi-Purpose Token) amounts. +% endif + * @note This is an untyped field. +% if field['requirement'] == 'soeREQUIRED': + * @return The field value. +% else: + * @return The field value, or std::nullopt if not present. +% endif + */ +% if field['requirement'] == 'soeREQUIRED': + [[nodiscard]] + ${field['typeData']['return_type']} + get${field['name'][2:]}() const + { + return this->tx_->${field['typeData']['getter_method']}(${field['name']}); + } +% else: + [[nodiscard]] + ${field['typeData']['return_type_optional']} + get${field['name'][2:]}() const + { + if (this->tx_->isFieldPresent(${field['name']})) + return this->tx_->${field['typeData']['getter_method']}(${field['name']}); + return std::nullopt; + } + + /** + * @brief Check if ${field['name']} is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + has${field['name'][2:]}() const + { + return this->tx_->isFieldPresent(${field['name']}); + } +% endif +% endif +% endfor +}; + +<% + required_fields = [f for f in fields if f['requirement'] == 'soeREQUIRED'] +%>\ +/** + * @brief Builder for ${name} transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class ${name}Builder : public TransactionBuilderBase<${name}Builder> +{ +public: + /** + * @brief Construct a new ${name}Builder with required fields. + * @param account The account initiating the transaction. +% for field in required_fields: + * @param ${field['paramName']} The ${field['name']} field value. +% endfor + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + ${name}Builder(SF_ACCOUNT::type::value_type account, +% for i, field in enumerate(required_fields): + ${field['typeData']['setter_type']} ${field['paramName']},\ +% endfor + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase<${name}Builder>(${tag}, account, sequence, fee) + { +% for field in required_fields: + set${field['name'][2:]}(${field['paramName']}); +% endfor + } + + /** + * @brief Construct a ${name}Builder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + ${name}Builder(std::shared_ptr tx) + { + if (tx->getTxnType() != ${tag}) + { + throw std::runtime_error("Invalid transaction type for ${name}Builder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ +% for field in fields: + + /** + * @brief Set ${field['name']} (${field['requirement']}) +% if field.get('supports_mpt'): + * @note This field supports MPT (Multi-Purpose Token) amounts. +% endif + * @return Reference to this builder for method chaining. + */ + ${name}Builder& + set${field['name'][2:]}(${field['typeData']['setter_type']} value) + { +% if field.get('stiSuffix') == 'ISSUE': + object_[${field['name']}] = STIssue(${field['name']}, value); +% elif field['typeData'].get('setter_use_brackets'): + object_[${field['name']}] = value; +% else: + object_.${field['typeData']['setter_method']}(${field['name']}, value); +% endif + return *this; + } +% endfor + + /** + * @brief Build and return the ${name} wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + ${name} + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return ${name}{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/scripts/templates/TransactionTests.cpp.mako b/scripts/templates/TransactionTests.cpp.mako new file mode 100644 index 0000000000..b6bd5cd831 --- /dev/null +++ b/scripts/templates/TransactionTests.cpp.mako @@ -0,0 +1,241 @@ +// Auto-generated unit tests for transaction ${name} +<% + required_fields = [f for f in fields if f["requirement"] == "soeREQUIRED"] + optional_fields = [f for f in fields if f["requirement"] != "soeREQUIRED"] + + def canonical_expr(field): + return f"canonical_{field['stiSuffix']}()" + + # Pick a wrong transaction to test type mismatch + if name != "AccountSet": + wrong_tx_include = "AccountSet" + else: + wrong_tx_include = "OfferCancel" +%> + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(Transactions${name}Tests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("test${name}")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values +% for field in fields: + auto const ${field["paramName"]}Value = ${canonical_expr(field)}; +% endfor + + ${name}Builder builder{ + accountValue, +% for field in required_fields: + ${field["paramName"]}Value, +% endfor + sequenceValue, + feeValue + }; + + // Set optional fields +% for field in optional_fields: + builder.set${field["name"][2:]}(${field["paramName"]}Value); +% endfor + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields +% for field in required_fields: + { + auto const& expected = ${field["paramName"]}Value; + auto const actual = tx.get${field["name"][2:]}(); + expectEqualField(expected, actual, "${field["name"]}"); + } + +% endfor + // Verify optional fields +% for field in optional_fields: + { + auto const& expected = ${field["paramName"]}Value; + auto const actualOpt = tx.get${field["name"][2:]}(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field ${field["name"]} should be present"; + expectEqualField(expected, *actualOpt, "${field["name"]}"); + EXPECT_TRUE(tx.has${field["name"][2:]}()); + } + +% endfor +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(Transactions${name}Tests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("test${name}FromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values +% for field in fields: + auto const ${field["paramName"]}Value = ${canonical_expr(field)}; +% endfor + + // Build an initial transaction + ${name}Builder initialBuilder{ + accountValue, +% for field in required_fields: + ${field["paramName"]}Value, +% endfor + sequenceValue, + feeValue + }; + +% for field in optional_fields: + initialBuilder.set${field["name"][2:]}(${field["paramName"]}Value); +% endfor + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + ${name}Builder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields +% for field in required_fields: + { + auto const& expected = ${field["paramName"]}Value; + auto const actual = rebuiltTx.get${field["name"][2:]}(); + expectEqualField(expected, actual, "${field["name"]}"); + } + +% endfor + // Verify optional fields +% for field in optional_fields: + { + auto const& expected = ${field["paramName"]}Value; + auto const actualOpt = rebuiltTx.get${field["name"][2:]}(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field ${field["name"]} should be present"; + expectEqualField(expected, *actualOpt, "${field["name"]}"); + } + +% endfor +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(Transactions${name}Tests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + +% if wrong_tx_include == "AccountSet": + ${wrong_tx_include}Builder wrongBuilder{account, 1, canonical_AMOUNT()}; +% else: + ${wrong_tx_include}Builder wrongBuilder{account, canonical_UINT32(), 1, canonical_AMOUNT()}; +% endif + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(${name}{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(Transactions${name}Tests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + +% if wrong_tx_include == "AccountSet": + ${wrong_tx_include}Builder wrongBuilder{account, 1, canonical_AMOUNT()}; +% else: + ${wrong_tx_include}Builder wrongBuilder{account, canonical_UINT32(), 1, canonical_AMOUNT()}; +% endif + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(${name}Builder{wrongTx.getSTTx()}, std::runtime_error); +} + +% if optional_fields: +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(Transactions${name}Tests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("test${name}Nullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values +% for field in required_fields: + auto const ${field["paramName"]}Value = ${canonical_expr(field)}; +% endfor + + ${name}Builder builder{ + accountValue, +% for field in required_fields: + ${field["paramName"]}Value, +% endfor + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present +% for field in optional_fields: + EXPECT_FALSE(tx.has${field["name"][2:]}()); + EXPECT_FALSE(tx.get${field["name"][2:]}().has_value()); +% endfor +} +% endif + +} diff --git a/src/libxrpl/basics/Archive.cpp b/src/libxrpl/basics/Archive.cpp index 06f19b27ee..e77dabcd68 100644 --- a/src/libxrpl/basics/Archive.cpp +++ b/src/libxrpl/basics/Archive.cpp @@ -20,7 +20,7 @@ extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const& Throw("Invalid source file"); using archive_ptr = std::unique_ptr; - archive_ptr ar{archive_read_new(), [](struct archive* a) { archive_read_free(a); }}; + archive_ptr const ar{archive_read_new(), [](struct archive* a) { archive_read_free(a); }}; if (!ar) Throw("Failed to allocate archive"); @@ -36,7 +36,8 @@ extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const& Throw(archive_error_string(ar.get())); } - archive_ptr aw{archive_write_disk_new(), [](struct archive* a) { archive_write_free(a); }}; + archive_ptr const aw{ + archive_write_disk_new(), [](struct archive* a) { archive_write_free(a); }}; if (!aw) Throw("Failed to allocate archive"); @@ -51,8 +52,8 @@ extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const& if (archive_write_disk_set_standard_lookup(aw.get()) < ARCHIVE_OK) Throw(archive_error_string(aw.get())); - int result; - struct archive_entry* entry; + int result = 0; + struct archive_entry* entry = nullptr; while (true) { result = archive_read_next_header(ar.get(), &entry); @@ -67,9 +68,9 @@ extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const& if (archive_entry_size(entry) > 0) { - void const* buf; - size_t sz; - la_int64_t offset; + void const* buf = nullptr; + size_t sz = 0; + la_int64_t offset = 0; while (true) { result = archive_read_data_block(ar.get(), &buf, &sz, &offset); diff --git a/src/libxrpl/basics/BasicConfig.cpp b/src/libxrpl/basics/BasicConfig.cpp index 63d3a56c40..ba10a575d9 100644 --- a/src/libxrpl/basics/BasicConfig.cpp +++ b/src/libxrpl/basics/BasicConfig.cpp @@ -55,7 +55,7 @@ Section::append(std::vector const& lines) val = ""; break; } - else if (val.at(comment - 1) == '\\') + if (val.at(comment - 1) == '\\') { // we have an escaped comment char. Erase the escape char // and keep looking @@ -83,9 +83,13 @@ Section::append(std::vector const& lines) boost::smatch match; if (boost::regex_match(line, match, re1)) + { set(match[1], match[2]); + } else + { values_.push_back(line); + } lines_.push_back(std::move(line)); } @@ -122,7 +126,7 @@ BasicConfig::section(std::string const& name) Section const& BasicConfig::section(std::string const& name) const { - static Section none(""); + static Section const none(""); auto const iter = map_.find(name); if (iter == map_.end()) return none; diff --git a/src/libxrpl/basics/FileUtilities.cpp b/src/libxrpl/basics/FileUtilities.cpp index 0f636276da..96ec3fa3ba 100644 --- a/src/libxrpl/basics/FileUtilities.cpp +++ b/src/libxrpl/basics/FileUtilities.cpp @@ -26,7 +26,7 @@ getFileContents( using namespace boost::filesystem; using namespace boost::system::errc; - path fullPath{canonical(sourcePath, ec)}; + path const fullPath{canonical(sourcePath, ec)}; if (ec) return {}; @@ -45,7 +45,7 @@ getFileContents( return {}; } - std::string const result{ + std::string result{ std::istreambuf_iterator{fileStream}, std::istreambuf_iterator{}}; if (fileStream.bad()) diff --git a/src/libxrpl/basics/Log.cpp b/src/libxrpl/basics/Log.cpp index 3e36b7d458..f0a546ee75 100644 --- a/src/libxrpl/basics/Log.cpp +++ b/src/libxrpl/basics/Log.cpp @@ -119,7 +119,7 @@ Logs::open(boost::filesystem::path const& pathToLogFile) beast::Journal::Sink& Logs::get(std::string const& name) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto const result = sinks_.emplace(name, makeSink(name, thresh_)); return *result.first->second; } @@ -145,7 +145,7 @@ Logs::threshold() const void Logs::threshold(beast::severities::Severity thresh) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); thresh_ = thresh; for (auto& sink : sinks_) sink.second->threshold(thresh); @@ -155,7 +155,7 @@ std::vector> Logs::partition_severities() const { std::vector> list; - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); list.reserve(sinks_.size()); for (auto const& [name, sink] : sinks_) list.emplace_back(name, toString(fromSeverity(sink->threshold()))); @@ -171,7 +171,7 @@ Logs::write( { std::string s; format(s, text, level, partition); - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); file_.writeln(s); if (!silent_) std::cerr << s << '\n'; @@ -183,7 +183,7 @@ Logs::write( std::string Logs::rotate() { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); bool const wasOpened = file_.closeAndReopen(); if (wasOpened) return "The log file was closed and reopened."; @@ -411,15 +411,19 @@ public: std::unique_ptr set(std::unique_ptr sink) { - std::lock_guard _(m_); + std::lock_guard const _(m_); using std::swap; swap(holder_, sink); if (holder_) + { sink_ = *holder_; + } else + { sink_ = beast::Journal::getNullSink(); + } return sink; } @@ -427,7 +431,7 @@ public: beast::Journal::Sink& get() { - std::lock_guard _(m_); + std::lock_guard const _(m_); return sink_.get(); } }; diff --git a/src/libxrpl/basics/MallocTrim.cpp b/src/libxrpl/basics/MallocTrim.cpp index 1b0932b39d..ed20ac6c94 100644 --- a/src/libxrpl/basics/MallocTrim.cpp +++ b/src/libxrpl/basics/MallocTrim.cpp @@ -51,7 +51,7 @@ parseStatmRSSkB(std::string const& statm) // /proc/self/statm format: size resident shared text lib data dt // We want the second field (resident) which is in pages std::istringstream iss(statm); - long size, resident; + long size = 0, resident = 0; if (!(iss >> size >> resident)) return -1; diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index 59a41157db..a8adabd5de 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -66,14 +66,12 @@ concept UnsignedMantissa = std::is_unsigned_v || std::is_same_v class Number::Guard { - std::uint64_t digits_; // 16 decimal guard digits - std::uint8_t xbit_ : 1; // has a non-zero digit been shifted off the end - std::uint8_t sbit_ : 1; // the sign of the guard digits + std::uint64_t digits_{0}; // 16 decimal guard digits + std::uint8_t xbit_ : 1 {0}; // has a non-zero digit been shifted off the end + std::uint8_t sbit_ : 1 {0}; // the sign of the guard digits public: - explicit Guard() : digits_{0}, xbit_{0}, sbit_{0} - { - } + explicit Guard() = default; // set & test the sign bit void @@ -96,7 +94,7 @@ public: // This enables the client to round towards nearest, and on // tie, round towards even. int - round() noexcept; + round() const noexcept; // Modify the result to the correctly rounded value template @@ -116,7 +114,7 @@ public: // Modify the result to the correctly rounded value void - doRound(rep& drops, std::string location); + doRound(rep& drops, std::string location) const; private: void @@ -163,7 +161,7 @@ Number::Guard::push(T d) noexcept inline unsigned Number::Guard::pop() noexcept { - unsigned d = (digits_ & 0xF000'0000'0000'0000) >> 60; + unsigned const d = (digits_ & 0xF000'0000'0000'0000) >> 60; digits_ <<= 4; return d; } @@ -173,7 +171,7 @@ Number::Guard::pop() noexcept // 0 if Guard is exactly half // 1 if Guard is greater than half int -Number::Guard::round() noexcept +Number::Guard::round() const noexcept { auto mode = Number::getround(); @@ -258,7 +256,7 @@ Number::Guard::doRoundUp( } bringIntoRange(negative, mantissa, exponent, minMantissa); if (exponent > maxExponent) - throw std::overflow_error(location); + Throw(std::string(location)); } template @@ -284,7 +282,7 @@ Number::Guard::doRoundDown( // Modify the result to the correctly rounded value void -Number::Guard::doRound(rep& drops, std::string location) +Number::Guard::doRound(rep& drops, std::string location) const { auto r = round(); if (r == 1 || (r == 0 && (drops & 1) == 1)) @@ -298,7 +296,7 @@ Number::Guard::doRound(rep& drops, std::string location) // or "(maxRep + 1) / 10", neither of which will round up when // converting to rep, though the latter might overflow _before_ // rounding. - throw std::overflow_error(location); // LCOV_EXCL_LINE + Throw(std::string(location)); // LCOV_EXCL_LINE } ++drops; } @@ -327,7 +325,7 @@ Number::externalToInternal(rep mantissa) // int128_t, negate that, and cast it back down to the internalrep // In practice, this is only going to cover the case of // std::numeric_limits::min(). - int128_t temp = mantissa; + int128_t const temp = mantissa; return static_cast(-temp); } @@ -532,7 +530,7 @@ Number::operator+=(Number const& y) uint128_t xm = mantissa_; auto xe = exponent_; - bool yn = y.negative_; + bool const yn = y.negative_; uint128_t ym = y.mantissa_; auto ye = y.exponent_; Guard g; @@ -646,14 +644,14 @@ Number::operator*=(Number const& y) // *m = mantissa // *e = exponent - bool xn = negative_; - int xs = xn ? -1 : 1; + bool const xn = negative_; + int const xs = xn ? -1 : 1; internalrep xm = mantissa_; auto xe = exponent_; - bool yn = y.negative_; - int ys = yn ? -1 : 1; - internalrep ym = y.mantissa_; + bool const yn = y.negative_; + int const ys = yn ? -1 : 1; + internalrep const ym = y.mantissa_; auto ye = y.exponent_; auto zm = uint128_t(xm) * uint128_t(ym); @@ -708,13 +706,13 @@ Number::operator/=(Number const& y) // *m = mantissa // *e = exponent - bool np = negative_; - int ns = (np ? -1 : 1); + bool const np = negative_; + int const ns = (np ? -1 : 1); auto nm = mantissa_; auto ne = exponent_; - bool dp = y.negative_; - int ds = (dp ? -1 : 1); + bool const dp = y.negative_; + int const ds = (dp ? -1 : 1); auto dm = y.mantissa_; auto de = y.exponent_; @@ -730,7 +728,7 @@ Number::operator/=(Number const& y) // f can be up to 10^(38-19) = 10^19 safely static_assert(smallRange.log == 15); static_assert(largeRange.log == 18); - bool small = Number::getMantissaScale() == MantissaRange::small; + bool const small = Number::getMantissaScale() == MantissaRange::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"); @@ -913,9 +911,13 @@ to_string(Number const& amount) // Assemble the output: if (pre_from == pre_to) + { ret.append(1, '0'); + } else + { ret.append(pre_from, pre_to); + } if (post_to != post_from) { @@ -978,8 +980,8 @@ root(Number f, unsigned d) auto const di = static_cast(d); auto ex = [e = e, di = di]() // Euclidean remainder of e/d { - int k = (e >= 0 ? e : e - (di - 1)) / di; - int k2 = e - k * di; + int const k = (e >= 0 ? e : e - (di - 1)) / di; + int const k2 = e - (k * di); if (k2 == 0) return 0; return di - k2; @@ -996,7 +998,7 @@ 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; + 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; diff --git a/src/libxrpl/basics/ResolverAsio.cpp b/src/libxrpl/basics/ResolverAsio.cpp index 7c0a93a930..305bdb6451 100644 --- a/src/libxrpl/basics/ResolverAsio.cpp +++ b/src/libxrpl/basics/ResolverAsio.cpp @@ -35,7 +35,6 @@ namespace xrpl { template class AsyncObject { -protected: AsyncObject() : m_pending(0) { } @@ -93,6 +92,8 @@ public: private: // The number of handlers pending. std::atomic m_pending; + + friend Derived; }; class ResolverAsioImpl : public ResolverAsio, public AsyncObject @@ -108,7 +109,7 @@ public: std::condition_variable m_cv; std::mutex m_mut; - bool m_asyncHandlersCompleted; + bool m_asyncHandlersCompleted{true}; std::atomic m_stop_called; std::atomic m_stopped; @@ -135,7 +136,6 @@ public: , m_io_context(io_context) , m_strand(boost::asio::make_strand(io_context)) , m_resolver(io_context) - , m_asyncHandlersCompleted(true) , m_stop_called(false) , m_stopped(true) { @@ -152,7 +152,7 @@ public: void asyncHandlersComplete() { - std::unique_lock lk{m_mut}; + std::unique_lock const lk{m_mut}; m_asyncHandlersCompleted = true; m_cv.notify_all(); } @@ -169,10 +169,10 @@ public: XRPL_ASSERT(m_stopped == true, "xrpl::ResolverAsioImpl::start : stopped"); XRPL_ASSERT(m_stop_called == false, "xrpl::ResolverAsioImpl::start : not stopping"); - if (m_stopped.exchange(false) == true) + if (m_stopped.exchange(false)) { { - std::lock_guard lk{m_mut}; + std::lock_guard const lk{m_mut}; m_asyncHandlersCompleted = false; } addReference(); @@ -182,7 +182,7 @@ public: void stop_async() override { - if (m_stop_called.exchange(true) == false) + if (!m_stop_called.exchange(true)) { boost::asio::dispatch( m_io_context, @@ -229,7 +229,7 @@ public: { XRPL_ASSERT(m_stop_called == true, "xrpl::ResolverAsioImpl::do_stop : stopping"); - if (m_stopped.exchange(true) == false) + if (!m_stopped.exchange(true)) { m_work.clear(); m_resolver.cancel(); @@ -271,7 +271,7 @@ public: m_strand, std::bind(&ResolverAsioImpl::do_work, this, CompletionCounter(this)))); } - HostAndPort + static HostAndPort parseName(std::string const& str) { // first attempt to parse as an endpoint (IP addr + port). @@ -319,7 +319,7 @@ public: void do_work(CompletionCounter) { - if (m_stop_called == true) + if (m_stop_called) return; // We don't have any work to do at this time @@ -327,7 +327,7 @@ public: return; std::string const name(m_work.front().names.back()); - HandlerType handler(m_work.front().handler); + HandlerType const handler(m_work.front().handler); m_work.front().names.pop_back(); @@ -367,14 +367,14 @@ public: { XRPL_ASSERT(!names.empty(), "xrpl::ResolverAsioImpl::do_resolve : names non-empty"); - if (m_stop_called == false) + if (!m_stop_called) { m_work.emplace_back(names, handler); JLOG(m_journal.debug()) << "Queued new job with " << names.size() << " tasks. " << m_work.size() << " jobs outstanding."; - if (m_work.size() > 0) + if (!m_work.empty()) { boost::asio::post( m_io_context, diff --git a/src/libxrpl/basics/StringUtilities.cpp b/src/libxrpl/basics/StringUtilities.cpp index 96b5cfdb9b..5e3b100b26 100644 --- a/src/libxrpl/basics/StringUtilities.cpp +++ b/src/libxrpl/basics/StringUtilities.cpp @@ -24,7 +24,7 @@ sqlBlobLiteral(Blob const& blob) { std::string j; - j.reserve(blob.size() * 2 + 3); + j.reserve((blob.size() * 2) + 3); j.push_back('X'); j.push_back('\''); boost::algorithm::hex(blob.begin(), blob.end(), std::back_inserter(j)); @@ -37,7 +37,7 @@ bool parseUrl(parsedURL& pUrl, std::string const& strUrl) { // scheme://username:password@hostname:port/rest - static boost::regex reUrl( + static boost::regex const reUrl( "(?i)\\`\\s*" // required scheme "([[:alpha:]][-+.[:alpha:][:digit:]]*?):" @@ -103,7 +103,7 @@ trim_whitespace(std::string str) std::optional to_uint64(std::string const& s) { - std::uint64_t result; + std::uint64_t result = 0; if (beast::lexicalCastChecked(result, s)) return result; return std::nullopt; diff --git a/src/libxrpl/basics/UptimeClock.cpp b/src/libxrpl/basics/UptimeClock.cpp index 521a6a1313..3d1664482b 100644 --- a/src/libxrpl/basics/UptimeClock.cpp +++ b/src/libxrpl/basics/UptimeClock.cpp @@ -9,14 +9,14 @@ namespace xrpl { std::atomic UptimeClock::now_{0}; // seconds since start std::atomic UptimeClock::stop_{false}; // stop update thread -// On rippled shutdown, cancel and wait for the update thread +// On xrpld shutdown, cancel and wait for the update thread UptimeClock::update_thread::~update_thread() { if (joinable()) { stop_ = true; // This join() may take up to a 1s, but happens only - // once at rippled shutdown. + // once at xrpld shutdown. join(); } } @@ -40,7 +40,7 @@ UptimeClock::start_clock() }}; } -// This actually measures time since first use, instead of since rippled start. +// This actually measures time since first use, instead of since xrpld start. // However the difference between these two epochs is a small fraction of a // second and unimportant. @@ -50,7 +50,7 @@ UptimeClock::now() // start the update thread on first use static auto const init = start_clock(); - // Return the number of seconds since rippled start + // Return the number of seconds since xrpld start return time_point{duration{now_}}; } diff --git a/src/libxrpl/basics/base64.cpp b/src/libxrpl/basics/base64.cpp index fa06ac2cdc..cf6af3db70 100644 --- a/src/libxrpl/basics/base64.cpp +++ b/src/libxrpl/basics/base64.cpp @@ -77,13 +77,13 @@ get_inverse() } /// Returns max chars needed to encode a base64 string -inline std::size_t constexpr encoded_size(std::size_t n) +std::size_t constexpr encoded_size(std::size_t n) { return 4 * ((n + 2) / 3); } /// Returns max bytes needed to decode a base64 string -inline std::size_t constexpr decoded_size(std::size_t n) +std::size_t constexpr decoded_size(std::size_t n) { return ((n / 4) * 3) + 2; } @@ -103,11 +103,11 @@ inline std::size_t constexpr decoded_size(std::size_t n) std::size_t encode(void* dest, void const* src, std::size_t len) { - char* out = static_cast(dest); + char* out = static_cast(dest); // NOLINT(misc-const-correctness) char const* in = static_cast(src); auto const tab = base64::get_alphabet(); - for (auto n = len / 3; n--;) + for (auto n = len / 3; n != 0u; --n) { *out++ = tab[(in[0] & 0xfc) >> 2]; *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)]; @@ -116,6 +116,7 @@ encode(void* dest, void const* src, std::size_t len) in += 3; } + // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (len % 3) { case 2: @@ -153,7 +154,7 @@ encode(void* dest, void const* src, std::size_t len) std::pair decode(void* dest, char const* src, std::size_t len) { - char* out = static_cast(dest); + char* out = static_cast(dest); // NOLINT(misc-const-correctness) auto in = reinterpret_cast(src); unsigned char c3[3]{}, c4[4]{}; int i = 0; @@ -161,14 +162,14 @@ decode(void* dest, char const* src, std::size_t len) auto const inverse = base64::get_inverse(); - while (len-- && *in != '=') + while (((len--) != 0u) && *in != '=') { auto const v = inverse[*in]; if (v == -1) break; ++in; c4[i] = v; - if (++i == 4) + if (++i; i == 4) { c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4); c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); @@ -180,7 +181,7 @@ decode(void* dest, char const* src, std::size_t len) } } - if (i) + if (i != 0) { c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4); c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); diff --git a/src/libxrpl/basics/make_SSLContext.cpp b/src/libxrpl/basics/make_SSLContext.cpp index 885275be9e..c5ff456d25 100644 --- a/src/libxrpl/basics/make_SSLContext.cpp +++ b/src/libxrpl/basics/make_SSLContext.cpp @@ -140,7 +140,7 @@ initAnonymous(boost::asio::ssl::context& context) auto const ts = std::time(nullptr) - (25 * 60 * 60); - int ret = std::strftime(buf, sizeof(buf) - 1, "%y%m%d000000Z", std::gmtime(&ts)); + int const ret = std::strftime(buf, sizeof(buf) - 1, "%y%m%d000000Z", std::gmtime(&ts)); buf[ret] = 0; @@ -239,6 +239,7 @@ initAuthenticated( { boost::system::error_code ec; + // NOLINTNEXTLINE(bugprone-unused-return-value) context.use_certificate_file(cert_file, boost::asio::ssl::context::pem, ec); if (ec) @@ -252,7 +253,7 @@ initAuthenticated( // VFALCO Replace fopen() with RAII FILE* f = fopen(chain_file.c_str(), "r"); - if (!f) + if (f == nullptr) { LogicError( "Problem opening SSL chain file" + @@ -271,9 +272,11 @@ initAuthenticated( if (!cert_set) { if (SSL_CTX_use_certificate(ssl, x) != 1) + { LogicError( "Problem retrieving SSL certificate from chain " "file."); + } cert_set = true; } @@ -298,6 +301,7 @@ initAuthenticated( { boost::system::error_code ec; + // NOLINTNEXTLINE(bugprone-unused-return-value) context.use_private_key_file(key_file, boost::asio::ssl::context::pem, ec); if (ec) diff --git a/src/libxrpl/beast/clock/basic_seconds_clock.cpp b/src/libxrpl/beast/clock/basic_seconds_clock.cpp index 5248657479..c928c8c579 100644 --- a/src/libxrpl/beast/clock/basic_seconds_clock.cpp +++ b/src/libxrpl/beast/clock/basic_seconds_clock.cpp @@ -16,7 +16,7 @@ class seconds_clock_thread { using Clock = basic_seconds_clock::Clock; - bool stop_; + bool stop_{false}; std::mutex mut_; std::condition_variable cv_; std::thread thread_; @@ -41,15 +41,14 @@ seconds_clock_thread::~seconds_clock_thread() XRPL_ASSERT( thread_.joinable(), "beast::seconds_clock_thread::~seconds_clock_thread : thread joinable"); { - std::lock_guard lock(mut_); + std::lock_guard const lock(mut_); stop_ = true; } // publish stop_ asap so if waiting thread times-out, it will see it cv_.notify_one(); thread_.join(); } -seconds_clock_thread::seconds_clock_thread() - : stop_{false}, tp_{Clock::now().time_since_epoch().count()} +seconds_clock_thread::seconds_clock_thread() : tp_{Clock::now().time_since_epoch().count()} { thread_ = std::thread(&seconds_clock_thread::run, this); } diff --git a/src/libxrpl/beast/core/CurrentThreadName.cpp b/src/libxrpl/beast/core/CurrentThreadName.cpp index 8e54a74e4d..6f22687dcc 100644 --- a/src/libxrpl/beast/core/CurrentThreadName.cpp +++ b/src/libxrpl/beast/core/CurrentThreadName.cpp @@ -95,11 +95,6 @@ setCurrentThreadNameImpl(std::string_view name) { std::cerr << "WARNING: Thread name \"" << name << "\" (length " << name.size() << ") exceeds maximum of " << maxThreadNameLength << " characters on Linux.\n"; - - XRPL_ASSERT( - false, - "beast::detail::setCurrentThreadNameImpl : Thread name exceeds " - "maximum length for Linux"); } #endif } diff --git a/src/libxrpl/beast/core/SemanticVersion.cpp b/src/libxrpl/beast/core/SemanticVersion.cpp index 06cd622722..0601690560 100644 --- a/src/libxrpl/beast/core/SemanticVersion.cpp +++ b/src/libxrpl/beast/core/SemanticVersion.cpp @@ -29,7 +29,7 @@ print_identifiers(SemanticVersion::identifier_list const& list) bool isNumeric(std::string const& s) { - int n; + int n = 0; // Must be convertible to an integer if (!lexicalCastChecked(n, s)) @@ -62,13 +62,13 @@ chopUInt(int& value, int limit, std::string& input) return std::isdigit(c, std::locale::classic()); }); - std::string item(input.begin(), left_iter); + std::string const item(input.begin(), left_iter); // Must not be empty if (item.empty()) return false; - int n; + int n = 0; // Must be convertible to an integer if (!lexicalCastChecked(n, item)) @@ -234,27 +234,43 @@ int compare(SemanticVersion const& lhs, SemanticVersion const& rhs) { if (lhs.majorVersion > rhs.majorVersion) + { return 1; - else if (lhs.majorVersion < rhs.majorVersion) + } + if (lhs.majorVersion < rhs.majorVersion) + { return -1; + } if (lhs.minorVersion > rhs.minorVersion) + { return 1; - else if (lhs.minorVersion < rhs.minorVersion) + } + if (lhs.minorVersion < rhs.minorVersion) + { return -1; + } if (lhs.patchVersion > rhs.patchVersion) + { return 1; - else if (lhs.patchVersion < rhs.patchVersion) + } + if (lhs.patchVersion < rhs.patchVersion) + { return -1; + } if (lhs.isPreRelease() || rhs.isPreRelease()) { // Pre-releases have a lower precedence if (lhs.isRelease() && rhs.isPreRelease()) + { return 1; - else if (lhs.isPreRelease() && rhs.isRelease()) + } + if (lhs.isPreRelease() && rhs.isRelease()) + { return -1; + } // Compare pre-release identifiers for (int i = 0; @@ -263,18 +279,26 @@ compare(SemanticVersion const& lhs, SemanticVersion const& rhs) { // A larger list of identifiers has a higher precedence if (i >= rhs.preReleaseIdentifiers.size()) + { return 1; - else if (i >= lhs.preReleaseIdentifiers.size()) + } + if (i >= lhs.preReleaseIdentifiers.size()) + { return -1; + } std::string const& left(lhs.preReleaseIdentifiers[i]); std::string const& right(rhs.preReleaseIdentifiers[i]); // Numeric identifiers have lower precedence if (!isNumeric(left) && isNumeric(right)) + { return 1; - else if (isNumeric(left) && !isNumeric(right)) + } + if (isNumeric(left) && !isNumeric(right)) + { return -1; + } if (isNumeric(left)) { @@ -284,15 +308,19 @@ compare(SemanticVersion const& lhs, SemanticVersion const& rhs) int const iRight(lexicalCastThrow(right)); if (iLeft > iRight) + { return 1; - else if (iLeft < iRight) + } + if (iLeft < iRight) + { return -1; + } } else { XRPL_ASSERT(!isNumeric(right), "beast::compare : both inputs non-numeric"); - int result = left.compare(right); + int const result = left.compare(right); if (result != 0) return result; diff --git a/src/libxrpl/beast/insight/Collector.cpp b/src/libxrpl/beast/insight/Collector.cpp index 37b199fa7f..d4a528473b 100644 --- a/src/libxrpl/beast/insight/Collector.cpp +++ b/src/libxrpl/beast/insight/Collector.cpp @@ -4,5 +4,5 @@ namespace beast { namespace insight { Collector::~Collector() = default; -} +} // namespace insight } // namespace beast diff --git a/src/libxrpl/beast/insight/Groups.cpp b/src/libxrpl/beast/insight/Groups.cpp index 2b4178a9ca..393deca101 100644 --- a/src/libxrpl/beast/insight/Groups.cpp +++ b/src/libxrpl/beast/insight/Groups.cpp @@ -98,7 +98,7 @@ public: Group::ptr const& get(std::string const& name) override { - std::pair result(m_items.emplace(name, Group::ptr())); + std::pair const result(m_items.emplace(name, Group::ptr())); Group::ptr& group(result.first->second); if (result.second) group = std::make_shared(name, m_collector); diff --git a/src/libxrpl/beast/insight/Hook.cpp b/src/libxrpl/beast/insight/Hook.cpp index d5e0a3439f..f74f3e8705 100644 --- a/src/libxrpl/beast/insight/Hook.cpp +++ b/src/libxrpl/beast/insight/Hook.cpp @@ -5,5 +5,5 @@ namespace beast { namespace insight { HookImpl::~HookImpl() = default; -} +} // namespace insight } // namespace beast diff --git a/src/libxrpl/beast/insight/StatsDCollector.cpp b/src/libxrpl/beast/insight/StatsDCollector.cpp index 143bc51bd8..7d80e1a052 100644 --- a/src/libxrpl/beast/insight/StatsDCollector.cpp +++ b/src/libxrpl/beast/insight/StatsDCollector.cpp @@ -104,8 +104,8 @@ private: std::shared_ptr m_impl; std::string m_name; - CounterImpl::value_type m_value; - bool m_dirty; + CounterImpl::value_type m_value{0}; + bool m_dirty{false}; }; //------------------------------------------------------------------------------ @@ -162,9 +162,9 @@ private: std::shared_ptr m_impl; std::string m_name; - GaugeImpl::value_type m_last_value; - GaugeImpl::value_type m_value; - bool m_dirty; + GaugeImpl::value_type m_last_value{0}; + GaugeImpl::value_type m_value{0}; + bool m_dirty{false}; }; //------------------------------------------------------------------------------ @@ -194,8 +194,8 @@ private: std::shared_ptr m_impl; std::string m_name; - MeterImpl::value_type m_value; - bool m_dirty; + MeterImpl::value_type m_value{0}; + bool m_dirty{false}; }; //------------------------------------------------------------------------------ @@ -293,14 +293,14 @@ public: void add(StatsDMetricBase& metric) { - std::lock_guard _(metricsLock_); + std::lock_guard const _(metricsLock_); metrics_.push_back(metric); } void remove(StatsDMetricBase& metric) { - std::lock_guard _(metricsLock_); + std::lock_guard const _(metricsLock_); metrics_.erase(metrics_.iterator_to(metric)); } @@ -352,7 +352,7 @@ public: } } - void + static void log(std::vector const& buffers) { (void)buffers; @@ -444,7 +444,7 @@ public: return; } - std::lock_guard _(metricsLock_); + std::lock_guard const _(metricsLock_); for (auto& m : metrics_) m.do_process(); @@ -470,6 +470,7 @@ public: m_io_context.run(); + // NOLINTNEXTLINE(bugprone-unused-return-value) m_socket.shutdown(boost::asio::ip::udp::socket::shutdown_send, ec); m_socket.close(); @@ -504,7 +505,7 @@ StatsDHookImpl::do_process() StatsDCounterImpl::StatsDCounterImpl( std::string const& name, std::shared_ptr const& impl) - : m_impl(impl), m_name(name), m_value(0), m_dirty(false) + : m_impl(impl), m_name(name) { m_impl->add(*this); } @@ -586,7 +587,7 @@ StatsDEventImpl::do_notify(EventImpl::value_type const& value) StatsDGaugeImpl::StatsDGaugeImpl( std::string const& name, std::shared_ptr const& impl) - : m_impl(impl), m_name(name), m_last_value(0), m_value(0), m_dirty(false) + : m_impl(impl), m_name(name) { m_impl->add(*this); } @@ -675,7 +676,7 @@ StatsDGaugeImpl::do_process() StatsDMeterImpl::StatsDMeterImpl( std::string const& name, std::shared_ptr const& impl) - : m_impl(impl), m_name(name), m_value(0), m_dirty(false) + : m_impl(impl), m_name(name) { m_impl->add(*this); } diff --git a/src/libxrpl/beast/net/IPAddressV6.cpp b/src/libxrpl/beast/net/IPAddressV6.cpp index a261002ed3..30e2eefb96 100644 --- a/src/libxrpl/beast/net/IPAddressV6.cpp +++ b/src/libxrpl/beast/net/IPAddressV6.cpp @@ -10,7 +10,7 @@ bool is_private(AddressV6 const& addr) { return ( - (addr.to_bytes()[0] & 0xfd) || // TODO fc00::/8 too ? + ((addr.to_bytes()[0] & 0xfd) != 0) || // TODO fc00::/8 too ? (addr.is_v4_mapped() && is_private(boost::asio::ip::make_address_v4(boost::asio::ip::v4_mapped, addr)))); } diff --git a/src/libxrpl/beast/net/IPEndpoint.cpp b/src/libxrpl/beast/net/IPEndpoint.cpp index 701181ef88..7e2799b46c 100644 --- a/src/libxrpl/beast/net/IPEndpoint.cpp +++ b/src/libxrpl/beast/net/IPEndpoint.cpp @@ -57,7 +57,7 @@ Endpoint::to_string() const if (port() != 0 && address().is_v6()) s += '['; s += address().to_string(); - if (port()) + if (port() != 0u) { if (address().is_v6()) s += ']'; @@ -95,10 +95,14 @@ operator>>(std::istream& is, Endpoint& endpoint) char i{0}; char readTo{0}; is.get(i); - if (i == '[') // we are an IPv6 endpoint + if (i == '[') + { // we are an IPv6 endpoint readTo = ']'; + } else + { addrStr += i; + } while (is && is.rdbuf()->in_avail() > 0 && is.get(i)) { @@ -107,7 +111,7 @@ operator>>(std::istream& is, Endpoint& endpoint) // so we continue to honor that here by assuming we are at the end // of the address portion if we hit a space (or the separator // we were expecting to see) - if (isspace(static_cast(i)) || (readTo && i == readTo)) + if ((isspace(static_cast(i)) != 0) || ((readTo != 0) && i == readTo)) break; if ((i == '.') || (i >= '0' && i <= ':') || (i >= 'a' && i <= 'f') || @@ -116,14 +120,13 @@ operator>>(std::istream& is, Endpoint& endpoint) addrStr += i; // don't exceed a reasonable length... - if (addrStr.size() == INET6_ADDRSTRLEN || - (readTo && readTo == ':' && addrStr.size() > 15)) + if (addrStr.size() == INET6_ADDRSTRLEN || (readTo == ':' && addrStr.size() > 15)) { is.setstate(std::ios_base::failbit); return is; } - if (!readTo && (i == '.' || i == ':')) + if ((readTo == 0) && (i == '.' || i == ':')) { // if we see a dot first, must be IPv4 // otherwise must be non-bracketed IPv6 @@ -141,7 +144,7 @@ operator>>(std::istream& is, Endpoint& endpoint) if (readTo == ']' && is.rdbuf()->in_avail() > 0) { is.get(i); - if (!(isspace(static_cast(i)) || i == ':')) + if ((isspace(static_cast(i)) == 0) && i != ':') { is.unget(); is.setstate(std::ios_base::failbit); @@ -159,14 +162,16 @@ operator>>(std::istream& is, Endpoint& endpoint) if (is.rdbuf()->in_avail() > 0) { - Port port; + Port port = 0; is >> port; if (is.fail()) return is; endpoint = Endpoint(addr, port); } else + { endpoint = Endpoint(addr); + } return is; } diff --git a/src/libxrpl/beast/utility/beast_Journal.cpp b/src/libxrpl/beast/utility/beast_Journal.cpp index f9ee0cdb73..7164fcfb06 100644 --- a/src/libxrpl/beast/utility/beast_Journal.cpp +++ b/src/libxrpl/beast/utility/beast_Journal.cpp @@ -124,9 +124,13 @@ Journal::ScopedStream::~ScopedStream() if (!s.empty()) { if (s == "\n") + { m_sink.write(m_level, ""); + } else + { m_sink.write(m_level, s); + } } } diff --git a/src/libxrpl/beast/utility/beast_PropertyStream.cpp b/src/libxrpl/beast/utility/beast_PropertyStream.cpp index f714ad8871..662d763ce0 100644 --- a/src/libxrpl/beast/utility/beast_PropertyStream.cpp +++ b/src/libxrpl/beast/utility/beast_PropertyStream.cpp @@ -15,7 +15,7 @@ namespace beast { // //------------------------------------------------------------------------------ -PropertyStream::Item::Item(Source* source) : m_source(source) +PropertyStream::Item::Item(Source* source) : ListNode(), m_source(source) { } @@ -151,14 +151,13 @@ PropertyStream::Set::stream() const // //------------------------------------------------------------------------------ -PropertyStream::Source::Source(std::string const& name) - : m_name(name), item_(this), parent_(nullptr) +PropertyStream::Source::Source(std::string const& name) : m_name(name), item_(this) { } PropertyStream::Source::~Source() { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); if (parent_ != nullptr) parent_->remove(*this); removeAll(); @@ -174,8 +173,8 @@ void PropertyStream::Source::add(Source& source) { std::lock(lock_, source.lock_); - std::lock_guard lk1(lock_, std::adopt_lock); - std::lock_guard lk2(source.lock_, std::adopt_lock); + std::lock_guard const lk1(lock_, std::adopt_lock); + std::lock_guard const lk2(source.lock_, std::adopt_lock); XRPL_ASSERT( source.parent_ == nullptr, "beast::PropertyStream::Source::add : null source parent"); @@ -187,8 +186,8 @@ void PropertyStream::Source::remove(Source& child) { std::lock(lock_, child.lock_); - std::lock_guard lk1(lock_, std::adopt_lock); - std::lock_guard lk2(child.lock_, std::adopt_lock); + std::lock_guard const lk1(lock_, std::adopt_lock); + std::lock_guard const lk2(child.lock_, std::adopt_lock); XRPL_ASSERT( child.parent_ == this, "beast::PropertyStream::Source::remove : child parent match"); @@ -199,10 +198,10 @@ PropertyStream::Source::remove(Source& child) void PropertyStream::Source::removeAll() { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); for (auto iter = children_.begin(); iter != children_.end();) { - std::lock_guard _cl((*iter)->lock_); + std::lock_guard const _cl((*iter)->lock_); remove(*(*iter)); } } @@ -222,7 +221,7 @@ PropertyStream::Source::write(PropertyStream& stream) Map map(m_name, stream); onWrite(map); - std::lock_guard _(lock_); + std::lock_guard const _(lock_); for (auto& child : children_) child.source().write(stream); @@ -237,9 +236,13 @@ PropertyStream::Source::write(PropertyStream& stream, std::string const& path) return; if (result.second) + { result.first->write(stream); + } else + { result.first->write_one(stream); + } } std::pair @@ -295,15 +298,19 @@ PropertyStream::Source::peel_name(std::string* path) if (path->empty()) return ""; - std::string::const_iterator first = (*path).begin(); - std::string::const_iterator last = (*path).end(); - std::string::const_iterator pos = std::find(first, last, '/'); + std::string::const_iterator const first = (*path).begin(); + std::string::const_iterator const last = (*path).end(); + std::string::const_iterator const pos = std::find(first, last, '/'); std::string s(first, pos); if (pos != last) + { *path = std::string(pos + 1, last); + } else + { *path = std::string(); + } return s; } @@ -312,11 +319,11 @@ PropertyStream::Source::peel_name(std::string* path) PropertyStream::Source* PropertyStream::Source::find_one_deep(std::string const& name) { - Source* found = find_one(name); + Source* found = find_one(name); // NOLINT(misc-const-correctness) if (found != nullptr) return found; - std::lock_guard _(lock_); + std::lock_guard const _(lock_); for (auto& s : children_) { found = s.source().find_one_deep(name); @@ -347,7 +354,7 @@ PropertyStream::Source::find_path(std::string path) PropertyStream::Source* PropertyStream::Source::find_one(std::string const& name) { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); for (auto& s : children_) { if (s.source().m_name == name) @@ -371,9 +378,13 @@ void PropertyStream::add(std::string const& key, bool value) { if (value) + { add(key, "true"); + } else + { add(key, "false"); + } } void @@ -464,9 +475,13 @@ void PropertyStream::add(bool value) { if (value) + { add("true"); + } else + { add("false"); + } } void diff --git a/src/libxrpl/core/HashRouter.cpp b/src/libxrpl/core/HashRouter.cpp index dff1394f77..f21daf84a2 100644 --- a/src/libxrpl/core/HashRouter.cpp +++ b/src/libxrpl/core/HashRouter.cpp @@ -22,7 +22,7 @@ HashRouter::emplace(uint256 const& key) -> std::pair void HashRouter::addSuppression(uint256 const& key) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); emplace(key); } @@ -36,7 +36,7 @@ HashRouter::addSuppressionPeer(uint256 const& key, PeerShortID peer) std::pair> HashRouter::addSuppressionPeerWithStatus(uint256 const& key, PeerShortID peer) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto result = emplace(key); result.first.addPeer(peer); @@ -46,7 +46,7 @@ HashRouter::addSuppressionPeerWithStatus(uint256 const& key, PeerShortID peer) bool HashRouter::addSuppressionPeer(uint256 const& key, PeerShortID peer, HashRouterFlags& flags) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto [s, created] = emplace(key); s.addPeer(peer); @@ -61,7 +61,7 @@ HashRouter::shouldProcess( HashRouterFlags& flags, std::chrono::seconds tx_interval) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto result = emplace(key); auto& s = result.first; @@ -73,7 +73,7 @@ HashRouter::shouldProcess( HashRouterFlags HashRouter::getFlags(uint256 const& key) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return emplace(key).first.getFlags(); } @@ -83,7 +83,7 @@ HashRouter::setFlags(uint256 const& key, HashRouterFlags flags) { XRPL_ASSERT(static_cast(flags), "xrpl::HashRouter::setFlags : valid input"); - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto& s = emplace(key).first; @@ -97,7 +97,7 @@ HashRouter::setFlags(uint256 const& key, HashRouterFlags flags) auto HashRouter::shouldRelay(uint256 const& key) -> std::optional> { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto& s = emplace(key).first; diff --git a/src/libxrpl/core/detail/JobQueue.cpp b/src/libxrpl/core/detail/JobQueue.cpp index 80fb8d2536..ddfc42c97e 100644 --- a/src/libxrpl/core/detail/JobQueue.cpp +++ b/src/libxrpl/core/detail/JobQueue.cpp @@ -13,9 +13,7 @@ JobQueue::JobQueue( Logs& logs, perf::PerfLog& perfLog) : m_journal(journal) - , m_lastJob(0) , m_invalidJobData(JobTypes::instance().getInvalid(), collector, logs) - , m_processCount(0) , m_workers(*this, &perfLog, "JobQueue", threadCount) , perfLog_(perfLog) , m_collector(collector) @@ -26,7 +24,7 @@ JobQueue::JobQueue( job_count = m_collector->make_gauge("job_count"); { - std::lock_guard lock(m_mutex); + std::lock_guard const lock(m_mutex); for (auto const& x : JobTypes::instance()) { @@ -52,7 +50,7 @@ JobQueue::~JobQueue() void JobQueue::collect() { - std::lock_guard lock(m_mutex); + std::lock_guard const lock(m_mutex); job_count = m_jobSet.size(); } @@ -78,14 +76,13 @@ JobQueue::addRefCountedJob(JobType type, std::string const& name, JobFunction co "requires no threads"); { - std::lock_guard lock(m_mutex); + std::lock_guard const lock(m_mutex); auto result = m_jobSet.emplace(type, name, ++m_lastJob, data.load(), func); auto const& job = *result.first; JobType const type(job.getType()); XRPL_ASSERT(type != jtINVALID, "xrpl::JobQueue::addRefCountedJob : has valid job type"); - XRPL_ASSERT( - m_jobSet.find(job) != m_jobSet.end(), "xrpl::JobQueue::addRefCountedJob : job found"); + XRPL_ASSERT(m_jobSet.contains(job), "xrpl::JobQueue::addRefCountedJob : job found"); perfLog_.jobQueue(type); JobTypeData& data(getJobTypeData(type)); @@ -107,9 +104,9 @@ JobQueue::addRefCountedJob(JobType type, std::string const& name, JobFunction co int JobQueue::getJobCount(JobType t) const { - std::lock_guard lock(m_mutex); + std::lock_guard const lock(m_mutex); - JobDataMap::const_iterator c = m_jobData.find(t); + JobDataMap::const_iterator const c = m_jobData.find(t); return (c == m_jobData.end()) ? 0 : c->second.waiting; } @@ -117,9 +114,9 @@ JobQueue::getJobCount(JobType t) const int JobQueue::getJobCountTotal(JobType t) const { - std::lock_guard lock(m_mutex); + std::lock_guard const lock(m_mutex); - JobDataMap::const_iterator c = m_jobData.find(t); + JobDataMap::const_iterator const c = m_jobData.find(t); return (c == m_jobData.end()) ? 0 : (c->second.waiting + c->second.running); } @@ -130,7 +127,7 @@ JobQueue::getJobCountGE(JobType t) const // return the number of jobs at this priority level or greater int ret = 0; - std::lock_guard lock(m_mutex); + std::lock_guard const lock(m_mutex); for (auto const& x : m_jobData) { @@ -144,7 +141,7 @@ JobQueue::getJobCountGE(JobType t) const std::unique_ptr JobQueue::makeLoadEvent(JobType t, std::string const& name) { - JobDataMap::iterator iter(m_jobData.find(t)); + JobDataMap::iterator const iter(m_jobData.find(t)); XRPL_ASSERT(iter != m_jobData.end(), "xrpl::JobQueue::makeLoadEvent : valid job type input"); if (iter == m_jobData.end()) @@ -159,7 +156,7 @@ JobQueue::addLoadEvents(JobType t, int count, std::chrono::milliseconds elapsed) if (isStopped()) LogicError("JobQueue::addLoadEvents() called after JobQueue stopped"); - JobDataMap::iterator iter(m_jobData.find(t)); + JobDataMap::iterator const iter(m_jobData.find(t)); XRPL_ASSERT(iter != m_jobData.end(), "xrpl::JobQueue::addLoadEvents : valid job type input"); iter->second.load().addSamples(count, elapsed); } @@ -182,7 +179,7 @@ JobQueue::getJson(int c) Json::Value priorities = Json::arrayValue; - std::lock_guard lock(m_mutex); + std::lock_guard const lock(m_mutex); for (auto& x : m_jobData) { @@ -193,10 +190,10 @@ JobQueue::getJson(int c) JobTypeData& data(x.second); - LoadMonitor::Stats stats(data.stats()); + LoadMonitor::Stats const stats(data.stats()); - int waiting(data.waiting); - int running(data.running); + int const waiting(data.waiting); + int const running(data.running); if ((stats.count != 0) || (waiting != 0) || (stats.latencyPeak != 0ms) || (running != 0)) { @@ -239,7 +236,7 @@ JobQueue::rendezvous() JobTypeData& JobQueue::getJobTypeData(JobType type) { - JobDataMap::iterator c(m_jobData.find(type)); + JobDataMap::iterator const c(m_jobData.find(type)); XRPL_ASSERT(c != m_jobData.end(), "xrpl::JobQueue::getJobTypeData : valid job type input"); // NIKB: This is ugly and I hate it. We must remove jtINVALID completely @@ -331,7 +328,7 @@ JobQueue::finishJob(JobType type) void JobQueue::processTask(int instance) { - JobType type; + JobType type = jtINVALID; { using namespace std::chrono; @@ -339,12 +336,12 @@ JobQueue::processTask(int instance) { Job job; { - std::lock_guard lock(m_mutex); + std::lock_guard const lock(m_mutex); getNextJob(job); ++m_processCount; } type = job.getType(); - JobTypeData& data(getJobTypeData(type)); + JobTypeData const& data(getJobTypeData(type)); JLOG(m_journal.trace()) << "Doing " << data.name() << "job"; // The amount of time that the job was in the queue @@ -366,7 +363,7 @@ JobQueue::processTask(int instance) } { - std::lock_guard lock(m_mutex); + std::lock_guard const lock(m_mutex); // Job should be destroyed before stopping // otherwise destructors with side effects can access // parent objects that are already destroyed. diff --git a/src/libxrpl/core/detail/LoadMonitor.cpp b/src/libxrpl/core/detail/LoadMonitor.cpp index 89a364ca30..e613717ed8 100644 --- a/src/libxrpl/core/detail/LoadMonitor.cpp +++ b/src/libxrpl/core/detail/LoadMonitor.cpp @@ -15,16 +15,14 @@ TODO //------------------------------------------------------------------------------ -LoadMonitor::Stats::Stats() : count(0), latencyAvg(0), latencyPeak(0), isOverloaded(false) +LoadMonitor::Stats::Stats() : latencyAvg(0), latencyPeak(0) { } //------------------------------------------------------------------------------ LoadMonitor::LoadMonitor(beast::Journal j) - : mCounts(0) - , mLatencyEvents(0) - , mLatencyMSAvg(0) + : mLatencyMSAvg(0) , mLatencyMSPeak(0) , mTargetLatencyAvg(0) , mTargetLatencyPk(0) @@ -104,7 +102,7 @@ LoadMonitor::addLoadSample(LoadEvent const& s) void LoadMonitor::addSamples(int count, std::chrono::milliseconds latency) { - std::lock_guard sl(mutex_); + std::lock_guard const sl(mutex_); update(); mCounts += count; @@ -136,12 +134,12 @@ LoadMonitor::isOverTarget(std::chrono::milliseconds avg, std::chrono::millisecon bool LoadMonitor::isOver() { - std::lock_guard sl(mutex_); + std::lock_guard const sl(mutex_); update(); if (mLatencyEvents == 0) - return 0; + return false; return isOverTarget( mLatencyMSAvg / (mLatencyEvents * 4), mLatencyMSPeak / (mLatencyEvents * 4)); @@ -153,7 +151,7 @@ LoadMonitor::getStats() using namespace std::chrono_literals; Stats stats; - std::lock_guard sl(mutex_); + std::lock_guard const sl(mutex_); update(); diff --git a/src/libxrpl/core/detail/Workers.cpp b/src/libxrpl/core/detail/Workers.cpp index a5e331bd44..ed9d09d1e8 100644 --- a/src/libxrpl/core/detail/Workers.cpp +++ b/src/libxrpl/core/detail/Workers.cpp @@ -1,5 +1,4 @@ #include -#include #include #include @@ -13,9 +12,7 @@ Workers::Workers( : m_callback(callback) , perfLog_(perfLog) , m_threadNames(threadNames) - , m_allPaused(true) , m_semaphore(0) - , m_numberOfThreads(0) , m_activeCount(0) , m_pauseCount(0) , m_runningTaskCount(0) @@ -47,7 +44,7 @@ Workers::setNumberOfThreads(int numberOfThreads) if (m_numberOfThreads == numberOfThreads) return; - if (perfLog_) + if (perfLog_ != nullptr) perfLog_->resizeJobs(numberOfThreads); if (numberOfThreads > m_numberOfThreads) @@ -96,11 +93,13 @@ Workers::stop() { setNumberOfThreads(0); + // Wait until all workers have paused AND no tasks are actively running. + // Both conditions are needed because m_allPaused (mutex-protected) and + // m_runningTaskCount (atomic) are not synchronized under the same lock, + // so m_allPaused can momentarily be true while a task is still finishing. std::unique_lock lk{m_mut}; - m_cv.wait(lk, [this] { return m_allPaused; }); + m_cv.wait(lk, [this] { return m_allPaused && numberOfCurrentlyRunningTasks() == 0; }); lk.unlock(); - - XRPL_ASSERT(numberOfCurrentlyRunningTasks() == 0, "xrpl::Workers::stop : zero running tasks"); } void @@ -120,7 +119,7 @@ Workers::deleteWorkers(beast::LockFreeStack& stack) { for (;;) { - Worker* const worker = stack.pop_front(); + Worker const* const worker = stack.pop_front(); if (worker != nullptr) { @@ -137,11 +136,8 @@ Workers::deleteWorkers(beast::LockFreeStack& stack) //------------------------------------------------------------------------------ Workers::Worker::Worker(Workers& workers, std::string const& threadName, int const instance) - : m_workers{workers} - , threadName_{threadName} - , instance_{instance} - , wakeCount_{0} - , shouldExit_{false} + : m_workers{workers}, threadName_{threadName}, instance_{instance} + { thread_ = std::thread{&Workers::Worker::run, this}; } @@ -149,7 +145,7 @@ Workers::Worker::Worker(Workers& workers, std::string const& threadName, int con Workers::Worker::~Worker() { { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; ++wakeCount_; shouldExit_ = true; } @@ -161,7 +157,7 @@ Workers::Worker::~Worker() void Workers::Worker::notify() { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; ++wakeCount_; wakeup_.notify_one(); } @@ -177,7 +173,7 @@ Workers::Worker::run() // if (++m_workers.m_activeCount == 1) { - std::lock_guard lk{m_workers.m_mut}; + std::lock_guard const lk{m_workers.m_mut}; m_workers.m_allPaused = false; } @@ -205,11 +201,9 @@ Workers::Worker::run() // We got paused break; } - else - { - // Undo our decrement - ++m_workers.m_pauseCount; - } + + // Undo our decrement + ++m_workers.m_pauseCount; } // We couldn't pause so we must have gotten @@ -217,7 +211,18 @@ Workers::Worker::run() // ++m_workers.m_runningTaskCount; m_workers.m_callback.processTask(instance_); - --m_workers.m_runningTaskCount; + + // When the running task count drops to zero, wake stop() which + // may be waiting for both m_allPaused and zero running tasks. + // Locking m_mut before notify_all() prevents a lost wakeup: + // it serializes against the predicate check inside stop()'s + // cv.wait(), ensuring the notification is not missed between + // the predicate evaluation and the actual sleep. + if (--m_workers.m_runningTaskCount == 0) + { + std::lock_guard const lk{m_workers.m_mut}; + m_workers.m_cv.notify_all(); + } } // Any worker that goes into the paused list must @@ -231,7 +236,7 @@ Workers::Worker::run() // if (--m_workers.m_activeCount == 0) { - std::lock_guard lk{m_workers.m_mut}; + std::lock_guard const lk{m_workers.m_mut}; m_workers.m_allPaused = true; m_workers.m_cv.notify_all(); } diff --git a/src/libxrpl/crypto/RFC1751.cpp b/src/libxrpl/crypto/RFC1751.cpp index 54d3e6f9d0..f7098f3833 100644 --- a/src/libxrpl/crypto/RFC1751.cpp +++ b/src/libxrpl/crypto/RFC1751.cpp @@ -198,10 +198,10 @@ char const* RFC1751::s_dictionary[2048] = { unsigned long RFC1751::extract(char const* s, int start, int length) { - unsigned char cl; - unsigned char cc; - unsigned char cr; - unsigned long x; + unsigned char cl = 0; + unsigned char cc = 0; + unsigned char cr = 0; + unsigned long x = 0; XRPL_ASSERT(length <= 11, "xrpl::RFC1751::extract : maximum length"); XRPL_ASSERT(start >= 0, "xrpl::RFC1751::extract : minimum start"); @@ -210,8 +210,8 @@ RFC1751::extract(char const* s, int start, int length) int const shiftR = 24 - (length + (start % 8)); cl = s[start / 8]; // get components - cc = (shiftR < 16) ? s[start / 8 + 1] : 0; - cr = (shiftR < 8) ? s[start / 8 + 2] : 0; + cc = (shiftR < 16) ? s[(start / 8) + 1] : 0; + cr = (shiftR < 8) ? s[(start / 8) + 2] : 0; x = ((long)(cl << 8 | cc) << 8 | cr); // Put bits together x = x >> shiftR; // Right justify number @@ -226,7 +226,7 @@ void RFC1751::btoe(std::string& strHuman, std::string const& strData) { char caBuffer[9]; /* add in room for the parity 2 bits*/ - int p, i; + int p = 0, i = 0; memcpy(caBuffer, strData.c_str(), 8); @@ -245,11 +245,11 @@ RFC1751::btoe(std::string& strHuman, std::string const& strData) void RFC1751::insert(char* s, int x, int start, int length) { - unsigned char cl; - unsigned char cc; - unsigned char cr; - unsigned long y; - int shift; + unsigned char cl = 0; + unsigned char cc = 0; + unsigned char cr = 0; + unsigned long y = 0; + int shift = 0; XRPL_ASSERT(length <= 11, "xrpl::RFC1751::insert : maximum length"); XRPL_ASSERT(start >= 0, "xrpl::RFC1751::insert : minimum start"); @@ -265,13 +265,13 @@ RFC1751::insert(char* s, int x, int start, int length) if (shift + length > 16) { s[start / 8] |= cl; - s[start / 8 + 1] |= cc; - s[start / 8 + 2] |= cr; + s[(start / 8) + 1] |= cc; + s[(start / 8) + 2] |= cr; } else if (shift + length > 8) { s[start / 8] |= cc; - s[start / 8 + 1] |= cr; + s[(start / 8) + 1] |= cr; } else { @@ -284,14 +284,22 @@ RFC1751::standard(std::string& strWord) { for (auto& letter : strWord) { - if (islower(static_cast(letter))) + if (islower(static_cast(letter)) != 0) + { letter = toupper(static_cast(letter)); + } else if (letter == '1') + { letter = 'L'; + } else if (letter == '0') + { letter = 'O'; + } else if (letter == '5') + { letter = 'S'; + } } } @@ -304,10 +312,10 @@ RFC1751::wsrch(std::string const& strWord, int iMin, int iMax) while (iResult < 0 && iMin != iMax) { // Have a range to search. - int iMid = iMin + (iMax - iMin) / 2; - int iDir = strWord.compare(s_dictionary[iMid]); + int const iMid = iMin + ((iMax - iMin) / 2); + int const iDir = strWord.compare(s_dictionary[iMid]); - if (!iDir) + if (iDir == 0) { iResult = iMid; // Found it. } @@ -336,12 +344,12 @@ RFC1751::etob(std::string& strData, std::vector vsHuman) if (6 != vsHuman.size()) return -1; - int i, p = 0; + int i = 0, p = 0; char b[9] = {0}; for (auto& strWord : vsHuman) { - int l = strWord.length(); + int const l = strWord.length(); if (l > 4 || l < 1) return -1; diff --git a/src/libxrpl/crypto/csprng.cpp b/src/libxrpl/crypto/csprng.cpp index 7858d4089c..343bed9be0 100644 --- a/src/libxrpl/crypto/csprng.cpp +++ b/src/libxrpl/crypto/csprng.cpp @@ -30,7 +30,7 @@ csprng_engine::~csprng_engine() void csprng_engine::mix_entropy(void* buffer, std::size_t count) { - std::array entropy; + std::array entropy{}; { // On every platform we support, std::random_device @@ -42,7 +42,7 @@ csprng_engine::mix_entropy(void* buffer, std::size_t count) e = rd(); } - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); // We add data to the pool, but we conservatively assume that // it contributes no actual entropy. @@ -71,7 +71,7 @@ csprng_engine::operator()(void* ptr, std::size_t count) csprng_engine::result_type csprng_engine::operator()() { - result_type ret; + result_type ret = 0; (*this)(&ret, sizeof(result_type)); return ret; } diff --git a/src/libxrpl/json/Writer.cpp b/src/libxrpl/json/Writer.cpp index be9595b088..ea7b4b51ae 100644 --- a/src/libxrpl/json/Writer.cpp +++ b/src/libxrpl/json/Writer.cpp @@ -25,7 +25,7 @@ std::map jsonSpecialCharacterEscape = { {'\r', "\\r"}, {'\t', "\\t"}}; -static size_t const jsonEscapeLength = 2; +size_t const jsonEscapeLength = 2; // All other JSON punctuation. char const closeBrace = '}'; @@ -36,7 +36,7 @@ char const openBrace = '{'; char const openBracket = '['; char const quote = '"'; -static auto const integralFloatsBecomeInts = false; +auto const integralFloatsBecomeInts = false; size_t lengthWithoutTrailingZeros(std::string const& s) @@ -80,7 +80,7 @@ public: void start(CollectionType ct) { - char ch = (ct == array) ? openBracket : openBrace; + char const ch = (ct == array) ? openBracket : openBrace; output({&ch, 1}); stack_.push(Collection()); stack_.top().type = ct; @@ -137,9 +137,13 @@ public: check(false, "Not an " + ((type == array ? "array: " : "object: ") + message)); } if (stack_.top().isFirst) + { stack_.top().isFirst = false; + } else + { output_({&comma, 1}); + } } void @@ -148,7 +152,7 @@ public: #ifndef NDEBUG // Make sure we haven't already seen this tag. auto& tags = stack_.top().tags; - check(tags.find(tag) == tags.end(), "Already seen tag " + tag); + check(!tags.contains(tag), "Already seen tag " + tag); tags.insert(tag); #endif @@ -196,7 +200,7 @@ private: explicit Collection() = default; /** What type of collection are we in? */ - Writer::CollectionType type; + Writer::CollectionType type = Writer::CollectionType::array; /** Is this the first entry in a collection? * If false, we have to emit a , before we write the next entry. */ diff --git a/src/libxrpl/json/json_reader.cpp b/src/libxrpl/json/json_reader.cpp index d28268454f..71365ba6c1 100644 --- a/src/libxrpl/json/json_reader.cpp +++ b/src/libxrpl/json/json_reader.cpp @@ -93,8 +93,8 @@ Reader::parse(char const* beginDoc, char const* endDoc, Value& root) nodes_.pop(); nodes_.push(&root); - bool successful = readValue(0); - Token token; + bool const successful = readValue(0); + Token token{}; skipCommentTokens(token); if (!root.isNull() && !root.isArray() && !root.isObject()) @@ -114,7 +114,7 @@ Reader::parse(char const* beginDoc, char const* endDoc, Value& root) bool Reader::readValue(unsigned depth) { - Token token; + Token token{}; skipCommentTokens(token); if (depth > nest_limit) return addError("Syntax error: maximum nesting depth exceeded", token); @@ -186,7 +186,7 @@ Reader::readToken(Token& token) { skipSpaces(); token.start_ = current_; - Char c = getNextChar(); + Char const c = getNextChar(); bool ok = true; switch (c) @@ -275,12 +275,16 @@ Reader::skipSpaces() { while (current_ != end_) { - Char c = *current_; + Char const c = *current_; if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + { ++current_; + } else + { break; + } } } @@ -292,9 +296,11 @@ Reader::match(Location pattern, int patternLength) int index = patternLength; - while (index--) + while ((index--) != 0) + { if (current_[index] != pattern[index]) return false; + } current_ += patternLength; return true; @@ -303,7 +309,7 @@ Reader::match(Location pattern, int patternLength) bool Reader::readComment() { - Char c = getNextChar(); + Char const c = getNextChar(); if (c == '*') return readCStyleComment(); @@ -319,7 +325,7 @@ Reader::readCStyleComment() { while (current_ != end_) { - Char c = getNextChar(); + Char const c = getNextChar(); if (c == '*' && *current_ == '/') break; @@ -333,7 +339,7 @@ Reader::readCppStyleComment() { while (current_ != end_) { - Char c = getNextChar(); + Char const c = getNextChar(); if (c == '\r' || c == '\n') break; @@ -356,7 +362,7 @@ Reader::readNumber() while (current_ != end_) { - if (!std::isdigit(static_cast(*current_))) + if (std::isdigit(static_cast(*current_)) == 0) { auto ret = std::find(std::begin(extended_tokens), std::end(extended_tokens), *current_); @@ -384,9 +390,13 @@ Reader::readString() c = getNextChar(); if (c == '\\') + { getNextChar(); + } else if (c == '"') + { break; + } } return c == '"'; @@ -395,7 +405,7 @@ Reader::readString() bool Reader::readObject(Token& tokenStart, unsigned depth) { - Token tokenName; + Token tokenName{}; std::string name; currentValue() = Value(objectValue); @@ -420,7 +430,7 @@ Reader::readObject(Token& tokenStart, unsigned depth) if (!decodeString(tokenName, name)) return recoverFromError(tokenObjectEnd); - Token colon; + Token colon{}; if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { @@ -434,13 +444,13 @@ Reader::readObject(Token& tokenStart, unsigned depth) Value& value = currentValue()[name]; nodes_.push(&value); - bool ok = readValue(depth + 1); + bool const ok = readValue(depth + 1); nodes_.pop(); if (!ok) // error already set return recoverFromError(tokenObjectEnd); - Token comma; + Token comma{}; if (!readToken(comma) || (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && @@ -470,7 +480,7 @@ Reader::readArray(Token& tokenStart, unsigned depth) if (*current_ == ']') // empty array { - Token endArray; + Token endArray{}; readToken(endArray); return true; } @@ -487,7 +497,7 @@ Reader::readArray(Token& tokenStart, unsigned depth) if (!ok) // error already set return recoverFromError(tokenArrayEnd); - Token token; + Token token{}; // Accept Comment after last item in the array. ok = readToken(token); @@ -496,7 +506,8 @@ Reader::readArray(Token& tokenStart, unsigned depth) ok = readToken(token); } - bool badTokenType = (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + bool const badTokenType = + (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); if (!ok || badTokenType) { @@ -515,7 +526,7 @@ bool Reader::decodeNumber(Token& token) { Location current = token.start_; - bool isNegative = *current == '-'; + bool const isNegative = *current == '-'; if (isNegative) ++current; @@ -536,7 +547,7 @@ Reader::decodeNumber(Token& token) while (current < token.end_ && (value <= Value::maxUInt)) { - Char c = *current++; + Char const c = *current++; if (c < '0' || c > '9') { @@ -578,9 +589,13 @@ Reader::decodeNumber(Token& token) // If it's representable as a signed integer, construct it as one. if (value <= Value::maxInt) + { currentValue() = static_cast(value); + } else + { currentValue() = static_cast(value); + } } return true; @@ -591,8 +606,8 @@ Reader::decodeDouble(Token& token) { double value = 0; int const bufferSize = 32; - int count; - int length = int(token.end_ - token.start_); + int count = 0; + int const length = int(token.end_ - token.start_); // Sanity check to avoid buffer overflow exploits. if (length < 0) { @@ -613,7 +628,7 @@ Reader::decodeDouble(Token& token) } else { - std::string buffer(token.start_, token.end_); + std::string const buffer(token.start_, token.end_); count = sscanf(buffer.c_str(), format, &value); } if (count != 1) @@ -643,16 +658,18 @@ Reader::decodeString(Token& token, std::string& decoded) while (current != end) { - Char c = *current++; + Char const c = *current++; if (c == '"') + { break; - else if (c == '\\') + } + if (c == '\\') { if (current == end) return addError("Empty escape sequence in string", token, current); - Char escape = *current++; + Char const escape = *current++; switch (escape) { @@ -689,7 +706,7 @@ Reader::decodeString(Token& token, std::string& decoded) break; case 'u': { - unsigned int unicode; + unsigned int unicode = 0; if (!decodeUnicodeCodePoint(token, current, end, unicode)) return false; @@ -721,29 +738,30 @@ Reader::decodeUnicodeCodePoint(Token& token, Location& current, Location end, un { // surrogate pairs if (end - current < 6) + { return addError( "additional six characters expected to parse unicode surrogate " "pair.", token, current); - - unsigned int surrogatePair; - - if (*(current++) == '\\' && *(current++) == 'u') - { - if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) - { - unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); - } - else - return false; } - else + + unsigned int surrogatePair = 0; + + if (*current != '\\' || *(current + 1) != 'u') + { return addError( - "expecting another \\u token to begin the second half of a " - "unicode surrogate pair", + "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current); + } + + current += 2; // skip two characters checked above + + if (!decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) + return false; + + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); } return true; @@ -757,28 +775,38 @@ Reader::decodeUnicodeEscapeSequence( unsigned int& unicode) { if (end - current < 4) + { return addError( "Bad unicode escape sequence in string: four digits expected.", token, current); + } unicode = 0; for (int index = 0; index < 4; ++index) { - Char c = *current++; + Char const c = *current++; unicode *= 16; if (c >= '0' && c <= '9') + { unicode += c - '0'; + } else if (c >= 'a' && c <= 'f') + { unicode += c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') + { unicode += c - 'A' + 10; + } else + { return addError( "Bad unicode escape sequence in string: hexadecimal digit " "expected.", token, current); + } } return true; @@ -798,8 +826,8 @@ Reader::addError(std::string const& message, Token& token, Location extra) bool Reader::recoverFromError(TokenType skipUntilToken) { - int errorCount = int(errors_.size()); - Token skip; + int const errorCount = int(errors_.size()); + Token skip{}; while (true) { @@ -845,7 +873,7 @@ Reader::getLocationLineAndColumn(Location location, int& line, int& column) cons while (current < location && current != end_) { - Char c = *current++; + Char const c = *current++; if (c == '\r') { @@ -870,7 +898,7 @@ Reader::getLocationLineAndColumn(Location location, int& line, int& column) cons std::string Reader::getLocationLineAndColumn(Location location) const { - int line, column; + int line = 0, column = 0; getLocationLineAndColumn(location, line, column); return "Line " + std::to_string(line) + ", Column " + std::to_string(column); } @@ -886,7 +914,7 @@ Reader::getFormattedErrorMessages() const formattedMessage += "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; formattedMessage += " " + error.message_ + "\n"; - if (error.extra_) + if (error.extra_ != nullptr) formattedMessage += "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; } @@ -897,7 +925,7 @@ std::istream& operator>>(std::istream& sin, Value& root) { Json::Reader reader; - bool ok = reader.parse(sin, root); + bool const ok = reader.parse(sin, root); // XRPL_ASSERT(ok, "Json::operator>>() : parse succeeded"); if (!ok) diff --git a/src/libxrpl/json/json_value.cpp b/src/libxrpl/json/json_value.cpp index 60197d75f0..d4351b23ef 100644 --- a/src/libxrpl/json/json_value.cpp +++ b/src/libxrpl/json/json_value.cpp @@ -40,10 +40,10 @@ public: // return 0; if (length == unknown) - length = value ? (unsigned int)strlen(value) : 0; + length = (value != nullptr) ? (unsigned int)strlen(value) : 0; char* newString = static_cast(malloc(length + 1)); - if (value) + if (value != nullptr) memcpy(newString, value, length); newString[length] = 0; return newString; @@ -52,7 +52,7 @@ public: void releaseStringValue(char* value) override { - if (value) + if (value != nullptr) free(value); } }; @@ -60,6 +60,7 @@ public: static ValueAllocator*& valueAllocator() { + // NOLINTNEXTLINE(misc-const-correctness) static ValueAllocator* valueAllocator = new DefaultValueAllocator; return valueAllocator; } @@ -98,21 +99,24 @@ Value::CZString::CZString(CZString const& other) other.index_ != noDuplication && other.cstr_ != 0 ? valueAllocator()->makeMemberName(other.cstr_) : other.cstr_) - , index_( - other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) : other.index_) + , index_([&]() -> int { + if (!other.cstr_) + return other.index_; + return other.index_ == noDuplication ? noDuplication : duplicate; + }()) { } Value::CZString::~CZString() { - if (cstr_ && index_ == duplicate) + if ((cstr_ != nullptr) && index_ == duplicate) valueAllocator()->releaseMemberName(const_cast(cstr_)); } bool Value::CZString::operator<(CZString const& other) const { - if (cstr_ && other.cstr_) + if ((cstr_ != nullptr) && (other.cstr_ != nullptr)) return strcmp(cstr_, other.cstr_) < 0; return index_ < other.index_; @@ -121,7 +125,7 @@ Value::CZString::operator<(CZString const& other) const bool Value::CZString::operator==(CZString const& other) const { - if (cstr_ && other.cstr_) + if ((cstr_ != nullptr) && (other.cstr_ != nullptr)) return strcmp(cstr_, other.cstr_) == 0; return index_ == other.index_; @@ -157,7 +161,7 @@ Value::CZString::isStaticString() const * memset( this, 0, sizeof(Value) ) * This optimization is used in ValueInternalMap fast allocator. */ -Value::Value(ValueType type) : type_(type), allocated_(0) +Value::Value(ValueType type) : type_(type) { switch (type) { @@ -225,7 +229,7 @@ Value::Value(std::string const& value) : type_(stringValue), allocated_(true) valueAllocator()->duplicateStringValue(value.c_str(), (unsigned int)value.length()); } -Value::Value(StaticString const& value) : type_(stringValue), allocated_(false) +Value::Value(StaticString const& value) : type_(stringValue) { value_.string_ = const_cast(value.c_str()); } @@ -248,13 +252,15 @@ Value::Value(Value const& other) : type_(other.type_) break; case stringValue: - if (other.value_.string_) + if (other.value_.string_ != nullptr) { value_.string_ = valueAllocator()->duplicateStringValue(other.value_.string_); allocated_ = true; } else + { value_.string_ = 0; + } break; @@ -289,7 +295,7 @@ Value::~Value() case arrayValue: case objectValue: - if (value_.map_) + if (value_.map_ != nullptr) delete value_.map_; break; @@ -328,11 +334,11 @@ Value::swap(Value& other) noexcept { std::swap(value_, other.value_); - ValueType temp = type_; + ValueType const temp = type_; type_ = other.type_; other.type_ = temp; - int temp2 = allocated_; + int const temp2 = allocated_; allocated_ = other.allocated_; other.allocated_ = temp2; } @@ -351,7 +357,9 @@ integerCmp(Int i, UInt ui) return -1; // Now we can safely compare. - return (i < ui) ? -1 : (i == ui) ? 0 : 1; + if (i < ui) + return -1; + return (i == ui) ? 0 : 1; } bool @@ -360,9 +368,13 @@ operator<(Value const& x, Value const& y) if (auto signum = x.type_ - y.type_) { if (x.type_ == intValue && y.type_ == uintValue) + { signum = integerCmp(x.value_.int_, y.value_.uint_); + } else if (x.type_ == uintValue && y.type_ == intValue) + { signum = -integerCmp(y.value_.int_, x.value_.uint_); + } return signum < 0; } @@ -381,16 +393,16 @@ operator<(Value const& x, Value const& y) return x.value_.real_ < y.value_.real_; case booleanValue: - return x.value_.bool_ < y.value_.bool_; + return static_cast(x.value_.bool_) < static_cast(y.value_.bool_); case stringValue: - return (x.value_.string_ == 0 && y.value_.string_) || - (y.value_.string_ && x.value_.string_ && + return (x.value_.string_ == 0 && (y.value_.string_ != nullptr)) || + ((y.value_.string_ != nullptr) && (x.value_.string_ != nullptr) && strcmp(x.value_.string_, y.value_.string_) < 0); case arrayValue: case objectValue: { - if (int signum = int(x.value_.map_->size()) - y.value_.map_->size()) + if (int const signum = int(x.value_.map_->size()) - y.value_.map_->size()) return signum < 0; return *x.value_.map_ < *y.value_.map_; @@ -402,7 +414,7 @@ operator<(Value const& x, Value const& y) // LCOV_EXCL_STOP } - return 0; // unreachable + return false; // unreachable } bool @@ -411,9 +423,9 @@ operator==(Value const& x, Value const& y) if (x.type_ != y.type_) { if (x.type_ == intValue && y.type_ == uintValue) - return !integerCmp(x.value_.int_, y.value_.uint_); + return integerCmp(x.value_.int_, y.value_.uint_) == 0; if (x.type_ == uintValue && y.type_ == intValue) - return !integerCmp(y.value_.int_, x.value_.uint_); + return integerCmp(y.value_.int_, x.value_.uint_) == 0; return false; } @@ -436,8 +448,8 @@ operator==(Value const& x, Value const& y) case stringValue: return x.value_.string_ == y.value_.string_ || - (y.value_.string_ && x.value_.string_ && - !strcmp(x.value_.string_, y.value_.string_)); + ((y.value_.string_ != nullptr) && (x.value_.string_ != nullptr) && + (strcmp(x.value_.string_, y.value_.string_) == 0)); case arrayValue: case objectValue: @@ -450,7 +462,7 @@ operator==(Value const& x, Value const& y) // LCOV_EXCL_STOP } - return 0; // unreachable + return false; // unreachable } char const* @@ -469,7 +481,7 @@ Value::asString() const return ""; case stringValue: - return value_.string_ ? value_.string_ : ""; + return (value_.string_ != nullptr) ? value_.string_ : ""; case booleanValue: return value_.bool_ ? "true" : "false"; @@ -514,7 +526,7 @@ Value::asInt() const case realValue: JSON_ASSERT_MESSAGE( - value_.real_ >= minInt && value_.real_ <= maxInt, + (value_.real_ >= minInt && value_.real_ <= maxInt), "Real out of signed integer range"); return Int(value_.real_); @@ -522,7 +534,7 @@ Value::asInt() const return value_.bool_ ? 1 : 0; case stringValue: { - char const* const str{value_.string_ ? value_.string_ : ""}; + char const* const str{(value_.string_ != nullptr) ? value_.string_ : ""}; return beast::lexicalCastThrow(str); } @@ -573,7 +585,7 @@ Value::asAbsUInt() const return value_.bool_ ? 1 : 0; case stringValue: { - char const* const str{value_.string_ ? value_.string_ : ""}; + char const* const str{(value_.string_ != nullptr) ? value_.string_ : ""}; auto const temp = beast::lexicalCastThrow(str); if (temp < 0) { @@ -615,14 +627,15 @@ Value::asUInt() const case realValue: JSON_ASSERT_MESSAGE( - value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range"); + (value_.real_ >= 0 && value_.real_ <= maxUInt), + "Real out of unsigned integer range"); return UInt(value_.real_); case booleanValue: return value_.bool_ ? 1 : 0; case stringValue: { - char const* const str{value_.string_ ? value_.string_ : ""}; + char const* const str{(value_.string_ != nullptr) ? value_.string_ : ""}; return beast::lexicalCastThrow(str); } @@ -692,11 +705,11 @@ Value::asBool() const return value_.bool_; case stringValue: - return value_.string_ && value_.string_[0] != 0; + return (value_.string_ != nullptr) && value_.string_[0] != 0; case arrayValue: case objectValue: - return value_.map_->size() != 0; + return !value_.map_->empty(); // LCOV_EXCL_START default: @@ -734,19 +747,19 @@ Value::isConvertibleTo(ValueType other) const other == realValue || other == stringValue || other == booleanValue; case booleanValue: - return (other == nullValue && value_.bool_ == false) || other == intValue || + return (other == nullValue && !value_.bool_) || other == intValue || other == uintValue || other == realValue || other == stringValue || other == booleanValue; case stringValue: return other == stringValue || - (other == nullValue && (!value_.string_ || value_.string_[0] == 0)); + (other == nullValue && ((value_.string_ == nullptr) || value_.string_[0] == 0)); case arrayValue: - return other == arrayValue || (other == nullValue && value_.map_->size() == 0); + return other == arrayValue || (other == nullValue && value_.map_->empty()); case objectValue: - return other == objectValue || (other == nullValue && value_.map_->size() == 0); + return other == objectValue || (other == nullValue && value_.map_->empty()); // LCOV_EXCL_START default: @@ -802,10 +815,10 @@ operator bool() const if (isString()) { auto s = asCString(); - return s && s[0]; + return (s != nullptr) && (s[0] != 0); } - return !(isArray() || isObject()) || size(); + return !(isArray() || isObject()) || (size() != 0u); } void @@ -836,13 +849,13 @@ Value::operator[](UInt index) if (type_ == nullValue) *this = Value(arrayValue); - CZString key(index); + CZString const key(index); ObjectValues::iterator it = value_.map_->lower_bound(key); if (it != value_.map_->end() && (*it).first == key) return (*it).second; - ObjectValues::value_type defaultValue(key, null); + ObjectValues::value_type const defaultValue(key, null); it = value_.map_->insert(it, defaultValue); return (*it).second; } @@ -857,8 +870,8 @@ Value::operator[](UInt index) const if (type_ == nullValue) return null; - CZString key(index); - ObjectValues::const_iterator it = value_.map_->find(key); + CZString const key(index); + ObjectValues::const_iterator const it = value_.map_->find(key); if (it == value_.map_->end()) return null; @@ -881,13 +894,13 @@ Value::resolveReference(char const* key, bool isStatic) if (type_ == nullValue) *this = Value(objectValue); - CZString actualKey(key, isStatic ? CZString::noDuplication : CZString::duplicateOnCopy); + CZString const actualKey(key, isStatic ? CZString::noDuplication : CZString::duplicateOnCopy); ObjectValues::iterator it = value_.map_->lower_bound(actualKey); if (it != value_.map_->end() && (*it).first == actualKey) return (*it).second; - ObjectValues::value_type defaultValue(actualKey, null); + ObjectValues::value_type const defaultValue(actualKey, null); it = value_.map_->insert(it, defaultValue); Value& value = (*it).second; return value; @@ -916,8 +929,8 @@ Value::operator[](char const* key) const if (type_ == nullValue) return null; - CZString actualKey(key, CZString::noDuplication); - ObjectValues::const_iterator it = value_.map_->find(actualKey); + CZString const actualKey(key, CZString::noDuplication); + ObjectValues::const_iterator const it = value_.map_->find(actualKey); if (it == value_.map_->end()) return null; @@ -983,8 +996,8 @@ Value::removeMember(char const* key) if (type_ == nullValue) return null; - CZString actualKey(key, CZString::noDuplication); - ObjectValues::iterator it = value_.map_->find(actualKey); + CZString const actualKey(key, CZString::noDuplication); + ObjectValues::iterator const it = value_.map_->find(actualKey); if (it == value_.map_->end()) return null; @@ -1034,7 +1047,7 @@ Value::getMemberNames() const Members members; members.reserve(value_.map_->size()); ObjectValues::const_iterator it = value_.map_->begin(); - ObjectValues::const_iterator itEnd = value_.map_->end(); + ObjectValues::const_iterator const itEnd = value_.map_->end(); for (; it != itEnd; ++it) members.push_back(std::string((*it).first.c_str())); @@ -1128,7 +1141,7 @@ Value::begin() const { case arrayValue: case objectValue: - if (value_.map_) + if (value_.map_ != nullptr) return const_iterator(value_.map_->begin()); break; @@ -1146,7 +1159,7 @@ Value::end() const { case arrayValue: case objectValue: - if (value_.map_) + if (value_.map_ != nullptr) return const_iterator(value_.map_->end()); break; @@ -1164,7 +1177,7 @@ Value::begin() { case arrayValue: case objectValue: - if (value_.map_) + if (value_.map_ != nullptr) return iterator(value_.map_->begin()); break; default: @@ -1181,7 +1194,7 @@ Value::end() { case arrayValue: case objectValue: - if (value_.map_) + if (value_.map_ != nullptr) return iterator(value_.map_->end()); break; default: diff --git a/src/libxrpl/json/json_valueiterator.cpp b/src/libxrpl/json/json_valueiterator.cpp index e40e674ee7..b3cf7e6538 100644 --- a/src/libxrpl/json/json_valueiterator.cpp +++ b/src/libxrpl/json/json_valueiterator.cpp @@ -13,7 +13,7 @@ namespace Json { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueIteratorBase::ValueIteratorBase() : current_(), isNull_(true) +ValueIteratorBase::ValueIteratorBase() : isNull_(true) { } @@ -89,7 +89,7 @@ ValueIteratorBase::key() const { Value::CZString const czString = (*current_).first; - if (czString.c_str()) + if (czString.c_str() != nullptr) { if (czString.isStaticString()) return Value(StaticString(czString.c_str())); @@ -105,7 +105,7 @@ ValueIteratorBase::index() const { Value::CZString const czString = (*current_).first; - if (!czString.c_str()) + if (czString.c_str() == nullptr) return czString.index(); return Value::UInt(-1); @@ -115,7 +115,7 @@ char const* ValueIteratorBase::memberName() const { char const* name = (*current_).first.c_str(); - return name ? name : ""; + return (name != nullptr) ? name : ""; } // ////////////////////////////////////////////////////////////////// diff --git a/src/libxrpl/json/json_writer.cpp b/src/libxrpl/json/json_writer.cpp index 13baf95845..150a7fe2e5 100644 --- a/src/libxrpl/json/json_writer.cpp +++ b/src/libxrpl/json/json_writer.cpp @@ -23,7 +23,7 @@ isControlCharacter(char ch) static bool containsControlCharacter(char const* str) { - while (*str) + while (*str != 0) { if (isControlCharacter(*(str++))) return true; @@ -47,8 +47,8 @@ std::string valueToString(Int value) { char buffer[32]; - char* current = buffer + sizeof(buffer); - bool isNegative = value < 0; + char* current = buffer + sizeof(buffer); // NOLINT(misc-const-correctness) + bool const isNegative = value < 0; if (isNegative) value = -value; @@ -66,7 +66,7 @@ std::string valueToString(UInt value) { char buffer[32]; - char* current = buffer + sizeof(buffer); + char* current = buffer + sizeof(buffer); // NOLINT(misc-const-correctness) uintToString(value, current); XRPL_ASSERT(current >= buffer, "Json::valueToString(UInt) : buffer check"); return current; @@ -106,7 +106,7 @@ valueToQuotedString(char const* value) // We have to walk value and escape any special characters. // Appending to std::string is not efficient, but this should be rare. // (Note: forward slashes are *not* rare, but I am not escaping them.) - unsigned maxsize = strlen(value) * 2 + 3; // all-escaped+quotes+NULL + unsigned const maxsize = (strlen(value) * 2) + 3; // all-escaped+quotes+NULL std::string result; result.reserve(maxsize); // to avoid lots of mallocs result += "\""; @@ -213,7 +213,7 @@ FastWriter::writeValue(Value const& value) case arrayValue: { document_ += "["; - int size = value.size(); + int const size = value.size(); for (int index = 0; index < size; ++index) { @@ -252,7 +252,7 @@ FastWriter::writeValue(Value const& value) // Class StyledWriter // ////////////////////////////////////////////////////////////////// -StyledWriter::StyledWriter() : rightMargin_(74), indentSize_(3) +StyledWriter::StyledWriter() { } @@ -304,7 +304,9 @@ StyledWriter::writeValue(Value const& value) Value::Members members(value.getMemberNames()); if (members.empty()) + { pushValue("{}"); + } else { writeWithIndent("{"); @@ -319,7 +321,7 @@ StyledWriter::writeValue(Value const& value) document_ += " : "; writeValue(childValue); - if (++it == members.end()) + if (++it; it == members.end()) break; document_ += ","; @@ -336,19 +338,21 @@ StyledWriter::writeValue(Value const& value) void StyledWriter::writeArrayValue(Value const& value) { - unsigned size = value.size(); + unsigned const size = value.size(); if (size == 0) + { pushValue("[]"); + } else { - bool isArrayMultiLine = isMultilineArray(value); + bool const isArrayMultiLine = isMultilineArray(value); if (isArrayMultiLine) { writeWithIndent("["); indent(); - bool hasChildValue = !childValues_.empty(); + bool const hasChildValue = !childValues_.empty(); unsigned index = 0; while (true) @@ -356,7 +360,9 @@ StyledWriter::writeArrayValue(Value const& value) Value const& childValue = value[index]; if (hasChildValue) + { writeWithIndent(childValues_[index]); + } else { writeIndent(); @@ -395,7 +401,7 @@ StyledWriter::writeArrayValue(Value const& value) bool StyledWriter::isMultilineArray(Value const& value) { - int size = value.size(); + int const size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); @@ -410,7 +416,7 @@ StyledWriter::isMultilineArray(Value const& value) { childValues_.reserve(size); addChildValues_ = true; - int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + int lineLength = 4 + ((size - 1) * 2); // '[ ' + ', '*n + ' ]' for (int index = 0; index < size; ++index) { @@ -429,9 +435,13 @@ void StyledWriter::pushValue(std::string const& value) { if (addChildValues_) + { childValues_.push_back(value); + } else + { document_ += value; + } } void @@ -439,7 +449,7 @@ StyledWriter::writeIndent() { if (!document_.empty()) { - char last = document_[document_.length() - 1]; + char const last = document_[document_.length() - 1]; if (last == ' ') // already indented return; @@ -476,8 +486,7 @@ StyledWriter::unindent() // Class StyledStreamWriter // ////////////////////////////////////////////////////////////////// -StyledStreamWriter::StyledStreamWriter(std::string indentation) - : document_(nullptr), rightMargin_(74), indentation_(indentation) +StyledStreamWriter::StyledStreamWriter(std::string indentation) : indentation_(indentation) { } @@ -529,7 +538,9 @@ StyledStreamWriter::writeValue(Value const& value) Value::Members members(value.getMemberNames()); if (members.empty()) + { pushValue("{}"); + } else { writeWithIndent("{"); @@ -561,19 +572,21 @@ StyledStreamWriter::writeValue(Value const& value) void StyledStreamWriter::writeArrayValue(Value const& value) { - unsigned size = value.size(); + unsigned const size = value.size(); if (size == 0) + { pushValue("[]"); + } else { - bool isArrayMultiLine = isMultilineArray(value); + bool const isArrayMultiLine = isMultilineArray(value); if (isArrayMultiLine) { writeWithIndent("["); indent(); - bool hasChildValue = !childValues_.empty(); + bool const hasChildValue = !childValues_.empty(); unsigned index = 0; while (true) @@ -581,7 +594,9 @@ StyledStreamWriter::writeArrayValue(Value const& value) Value const& childValue = value[index]; if (hasChildValue) + { writeWithIndent(childValues_[index]); + } else { writeIndent(); @@ -620,7 +635,7 @@ StyledStreamWriter::writeArrayValue(Value const& value) bool StyledStreamWriter::isMultilineArray(Value const& value) { - int size = value.size(); + int const size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); @@ -635,7 +650,7 @@ StyledStreamWriter::isMultilineArray(Value const& value) { childValues_.reserve(size); addChildValues_ = true; - int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + int lineLength = 4 + ((size - 1) * 2); // '[ ' + ', '*n + ' ]' for (int index = 0; index < size; ++index) { @@ -654,9 +669,13 @@ void StyledStreamWriter::pushValue(std::string const& value) { if (addChildValues_) + { childValues_.push_back(value); + } else + { *document_ << value; + } } void diff --git a/src/libxrpl/ledger/AcceptedLedgerTx.cpp b/src/libxrpl/ledger/AcceptedLedgerTx.cpp index f0d243f9b6..005d48e6f6 100644 --- a/src/libxrpl/ledger/AcceptedLedgerTx.cpp +++ b/src/libxrpl/ledger/AcceptedLedgerTx.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include diff --git a/src/libxrpl/ledger/ApplyStateTable.cpp b/src/libxrpl/ledger/ApplyStateTable.cpp index e31b39b437..9ebbca8ac5 100644 --- a/src/libxrpl/ledger/ApplyStateTable.cpp +++ b/src/libxrpl/ledger/ApplyStateTable.cpp @@ -107,7 +107,7 @@ ApplyStateTable::apply( Mods newMod; for (auto& item : items_) { - SField const* type; + SField const* type = nullptr; switch (item.second.first) { default: @@ -127,8 +127,8 @@ ApplyStateTable::apply( auto curNode = item.second.second; if ((type == &sfModifiedNode) && (*curNode == *origNode)) continue; - std::uint16_t nodeType = curNode ? curNode->getFieldU16(sfLedgerEntryType) - : origNode->getFieldU16(sfLedgerEntryType); + std::uint16_t const nodeType = curNode ? curNode->getFieldU16(sfLedgerEntryType) + : origNode->getFieldU16(sfLedgerEntryType); meta.setAffectedNode(item.first, *type, nodeType); if (type == &sfDeletedNode) { @@ -169,9 +169,11 @@ ApplyStateTable::apply( "xrpl::detail::ApplyStateTable::apply : valid nodes for " "modification"); - if (curNode->isThreadedType(to.rules())) // thread transaction to node - // item modified + if (curNode->isThreadedType(to.rules())) + { // thread transaction to node + // item modified threadItem(meta, curNode); + } STObject prevs(sfPreviousFields); for (auto const& obj : *origNode) @@ -273,9 +275,7 @@ ApplyStateTable::exists(ReadView const& base, Keylet const& k) const case Action::modify: break; } - if (!k.check(*sle)) - return false; - return true; + return k.check(*sle); } auto @@ -513,7 +513,7 @@ void ApplyStateTable::threadItem(TxMeta& meta, std::shared_ptr const& sle) { key_type prevTxID; - LedgerIndex prevLgrID; + LedgerIndex prevLgrID = 0; if (!sle->thread(meta.getTxID(), meta.getLgrSeq(), prevTxID, prevLgrID)) return; diff --git a/src/libxrpl/ledger/ApplyView.cpp b/src/libxrpl/ledger/ApplyView.cpp index 463b2ba538..476b635511 100644 --- a/src/libxrpl/ledger/ApplyView.cpp +++ b/src/libxrpl/ledger/ApplyView.cpp @@ -32,11 +32,11 @@ createRoot( auto findPreviousPage(ApplyView& view, Keylet const& directory, SLE::ref start) { - std::uint64_t page = start->getFieldU64(sfIndexPrevious); + std::uint64_t const page = start->getFieldU64(sfIndexPrevious); auto node = start; - if (page) + if (page != 0u) { node = view.peek(keylet::page(directory, page)); if (!node) diff --git a/src/libxrpl/ledger/BookDirs.cpp b/src/libxrpl/ledger/BookDirs.cpp index 4ba5945e8e..2bdf6ac9a5 100644 --- a/src/libxrpl/ledger/BookDirs.cpp +++ b/src/libxrpl/ledger/BookDirs.cpp @@ -1,5 +1,6 @@ #include #include +#include #include namespace xrpl { @@ -74,8 +75,10 @@ BookDirs::const_iterator::operator++() XRPL_ASSERT(index_ != zero, "xrpl::BookDirs::const_iterator::operator++ : nonzero index"); if (!cdirNext(*view_, cur_key_, sle_, entry_, index_)) { - if (index_ != 0 || - (cur_key_ = view_->succ(++cur_key_, next_quality_).value_or(zero)) == zero) + if (index_ == 0) + cur_key_ = view_->succ(++cur_key_, next_quality_).value_or(zero); + + if (index_ != 0 || cur_key_ == zero) { cur_key_ = key_; entry_ = 0; @@ -84,9 +87,7 @@ BookDirs::const_iterator::operator++() else if (!cdirFirst(*view_, cur_key_, sle_, entry_, index_)) { // LCOV_EXCL_START - UNREACHABLE( - "xrpl::BookDirs::const_iterator::operator++ : directory is " - "empty"); + UNREACHABLE("xrpl::BookDirs::const_iterator::operator++ : directory is empty"); // LCOV_EXCL_STOP } } diff --git a/src/libxrpl/ledger/BookListeners.cpp b/src/libxrpl/ledger/BookListeners.cpp index 9ba25b783a..8699d891a0 100644 --- a/src/libxrpl/ledger/BookListeners.cpp +++ b/src/libxrpl/ledger/BookListeners.cpp @@ -5,21 +5,21 @@ namespace xrpl { void BookListeners::addSubscriber(InfoSub::ref sub) { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); mListeners[sub->getSeq()] = sub; } void BookListeners::removeSubscriber(std::uint64_t seq) { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); mListeners.erase(seq); } void BookListeners::publish(MultiApiJson const& jvObj, hash_set& havePublished) { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); auto it = mListeners.cbegin(); while (it != mListeners.cend()) @@ -38,7 +38,9 @@ BookListeners::publish(MultiApiJson const& jvObj, hash_set& haveP ++it; } else + { it = mListeners.erase(it); + } } } diff --git a/src/libxrpl/ledger/CachedView.cpp b/src/libxrpl/ledger/CachedView.cpp index 12704d227e..aa075d69a4 100644 --- a/src/libxrpl/ledger/CachedView.cpp +++ b/src/libxrpl/ledger/CachedView.cpp @@ -21,7 +21,7 @@ CachedViewImpl::read(Keylet const& k) const auto const digest = [&]() -> std::optional { { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto const iter = map_.find(k.key); if (iter != map_.end()) { @@ -40,18 +40,24 @@ CachedViewImpl::read(Keylet const& k) const // If the sle is null, then a failure must have occurred in base_.read() XRPL_ASSERT(sle || baseRead, "xrpl::CachedView::read : null SLE result from base"); if (cacheHit && baseRead) + { hitsexpired.increment(); + } else if (cacheHit) + { hits.increment(); + } else + { misses.increment(); + } if (!cacheHit) { // Avoid acquiring this lock unless necessary. It is only necessary if // the key was not found in the map_. The lock is needed to add the key // and digest. - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); map_.emplace(k.key, *digest); } if (!sle || !k.check(*sle)) diff --git a/src/xrpld/app/misc/CanonicalTXSet.cpp b/src/libxrpl/ledger/CanonicalTXSet.cpp similarity index 97% rename from src/xrpld/app/misc/CanonicalTXSet.cpp rename to src/libxrpl/ledger/CanonicalTXSet.cpp index f971c100eb..72f45731fb 100644 --- a/src/xrpld/app/misc/CanonicalTXSet.cpp +++ b/src/libxrpl/ledger/CanonicalTXSet.cpp @@ -1,4 +1,4 @@ -#include +#include namespace xrpl { diff --git a/src/libxrpl/ledger/Credit.cpp b/src/libxrpl/ledger/Credit.cpp deleted file mode 100644 index 0a0283c3b8..0000000000 --- a/src/libxrpl/ledger/Credit.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include -#include -#include - -namespace xrpl { - -STAmount -creditLimit( - ReadView const& view, - AccountID const& account, - AccountID const& issuer, - Currency const& currency) -{ - STAmount result(Issue{currency, account}); - - auto sleRippleState = view.read(keylet::line(account, issuer, currency)); - - if (sleRippleState) - { - result = sleRippleState->getFieldAmount(account < issuer ? sfLowLimit : sfHighLimit); - result.setIssuer(account); - } - - XRPL_ASSERT(result.getIssuer() == account, "xrpl::creditLimit : result issuer match"); - XRPL_ASSERT(result.getCurrency() == currency, "xrpl::creditLimit : result currency match"); - return result; -} - -IOUAmount -creditLimit2(ReadView const& v, AccountID const& acc, AccountID const& iss, Currency const& cur) -{ - return toAmount(creditLimit(v, acc, iss, cur)); -} - -STAmount -creditBalance( - ReadView const& view, - AccountID const& account, - AccountID const& issuer, - Currency const& currency) -{ - STAmount result(Issue{currency, account}); - - auto sleRippleState = view.read(keylet::line(account, issuer, currency)); - - if (sleRippleState) - { - result = sleRippleState->getFieldAmount(sfBalance); - if (account < issuer) - result.negate(); - result.setIssuer(account); - } - - XRPL_ASSERT(result.getIssuer() == account, "xrpl::creditBalance : result issuer match"); - XRPL_ASSERT(result.getCurrency() == currency, "xrpl::creditBalance : result currency match"); - return result; -} - -} // namespace xrpl diff --git a/src/xrpld/app/ledger/Ledger.cpp b/src/libxrpl/ledger/Ledger.cpp similarity index 73% rename from src/xrpld/app/ledger/Ledger.cpp rename to src/libxrpl/ledger/Ledger.cpp index bde5ca010a..299a82a1f2 100644 --- a/src/xrpld/app/ledger/Ledger.cpp +++ b/src/libxrpl/ledger/Ledger.cpp @@ -1,19 +1,9 @@ -#include -#include -#include -#include -#include -#include -#include - #include #include #include -#include -#include #include -#include -#include +#include +#include #include #include #include @@ -21,7 +11,6 @@ #include #include #include -#include #include #include @@ -30,23 +19,6 @@ namespace xrpl { create_genesis_t const create_genesis{}; -uint256 -calculateLedgerHash(LedgerHeader const& info) -{ - // VFALCO This has to match addRaw in View.h. - return sha512Half( - HashPrefix::ledgerMaster, - std::uint32_t(info.seq), - std::uint64_t(info.drops.drops()), - info.parentHash, - info.txHash, - info.accountHash, - std::uint32_t(info.parentCloseTime.time_since_epoch().count()), - std::uint32_t(info.closeTime.time_since_epoch().count()), - std::uint8_t(info.closeTimeResolution.count()), - std::uint8_t(info.closeFlags)); -} - //------------------------------------------------------------------------------ class Ledger::sles_iter_impl : public sles_type::iter_base @@ -108,8 +80,7 @@ public: txs_iter_impl(txs_iter_impl const&) = default; - txs_iter_impl(bool metadata, SHAMap::const_iterator iter) - : metadata_(metadata), iter_(std::move(iter)) + txs_iter_impl(bool metadata, SHAMap::const_iterator iter) : metadata_(metadata), iter_(iter) { } @@ -138,8 +109,8 @@ public: { auto const& item = *iter_; if (metadata_) - return deserializeTxPlusMeta(item); - return {deserializeTx(item), nullptr}; + return Ledger::deserializeTxPlusMeta(item); + return {Ledger::deserializeTx(item), nullptr}; } }; @@ -147,13 +118,15 @@ public: Ledger::Ledger( create_genesis_t, - Config const& config, + Rules const& rules, + Fees const& fees, std::vector const& amendments, Family& family) : mImmutable(false) , txMap_(SHAMapType::TRANSACTION, family) , stateMap_(SHAMapType::STATE, family) - , rules_{config.features} + , fees_(fees) + , rules_(rules) , j_(beast::Journal(beast::Journal::getNullSink())) { header_.seq = 1; @@ -182,19 +155,19 @@ Ledger::Ledger( // Whether featureXRPFees is supported will depend on startup options. if (std::find(amendments.begin(), amendments.end(), featureXRPFees) != amendments.end()) { - sle->at(sfBaseFeeDrops) = config.FEES.reference_fee; - sle->at(sfReserveBaseDrops) = config.FEES.account_reserve; - sle->at(sfReserveIncrementDrops) = config.FEES.owner_reserve; + sle->at(sfBaseFeeDrops) = fees.base; + sle->at(sfReserveBaseDrops) = fees.reserve; + sle->at(sfReserveIncrementDrops) = fees.increment; } else { - if (auto const f = config.FEES.reference_fee.dropsAs()) + if (auto const f = fees.base.dropsAs()) sle->at(sfBaseFee) = *f; - if (auto const f = config.FEES.account_reserve.dropsAs()) + if (auto const f = fees.reserve.dropsAs()) sle->at(sfReserveBase) = *f; - if (auto const f = config.FEES.owner_reserve.dropsAs()) + if (auto const f = fees.increment.dropsAs()) sle->at(sfReserveIncrement) = *f; - sle->at(sfReferenceFeeUnits) = Config::FEE_UNITS_DEPRECATED; + sle->at(sfReferenceFeeUnits) = FEE_UNITS_DEPRECATED; } rawInsert(sle); } @@ -207,13 +180,15 @@ Ledger::Ledger( LedgerHeader const& info, bool& loaded, bool acquire, - Config const& config, + Rules const& rules, + Fees const& fees, Family& family, beast::Journal j) : mImmutable(true) , txMap_(SHAMapType::TRANSACTION, info.txHash, family) , stateMap_(SHAMapType::STATE, info.accountHash, family) - , rules_(config.features) + , fees_(fees) + , rules_(rules) , header_(info) , j_(j) { @@ -235,7 +210,6 @@ Ledger::Ledger( txMap_.setImmutable(); stateMap_.setImmutable(); - defaultFees(config); if (!setup()) loaded = false; @@ -275,11 +249,11 @@ Ledger::Ledger(Ledger const& prevLedger, NetClock::time_point closeTime) } } -Ledger::Ledger(LedgerHeader const& info, Config const& config, Family& family) +Ledger::Ledger(LedgerHeader const& info, Rules const& rules, Family& family) : mImmutable(true) , txMap_(SHAMapType::TRANSACTION, info.txHash, family) , stateMap_(SHAMapType::STATE, info.accountHash, family) - , rules_{config.features} + , rules_(rules) , header_(info) , j_(beast::Journal(beast::Journal::getNullSink())) { @@ -289,18 +263,19 @@ Ledger::Ledger(LedgerHeader const& info, Config const& config, Family& family) Ledger::Ledger( std::uint32_t ledgerSeq, NetClock::time_point closeTime, - Config const& config, + Rules const& rules, + Fees const& fees, Family& family) : mImmutable(false) , txMap_(SHAMapType::TRANSACTION, family) , stateMap_(SHAMapType::STATE, family) - , rules_{config.features} + , fees_(fees) + , rules_(rules) , j_(beast::Journal(beast::Journal::getNullSink())) { header_.seq = ledgerSeq; header_.closeTime = closeTime; header_.closeTimeResolution = ledgerDefaultTimeResolution; - defaultFees(config); setup(); } @@ -350,14 +325,14 @@ Ledger::addSLE(SLE const& sle) //------------------------------------------------------------------------------ std::shared_ptr -deserializeTx(SHAMapItem const& item) +Ledger::deserializeTx(SHAMapItem const& item) { SerialIter sit(item.slice()); return std::make_shared(sit); } std::pair, std::shared_ptr> -deserializeTxPlusMeta(SHAMapItem const& item) +Ledger::deserializeTxPlusMeta(SHAMapItem const& item) { std::pair, std::shared_ptr> result; SerialIter sit(item.slice()); @@ -597,9 +572,13 @@ Ledger::setup() if (src) { if (src->native()) + { dest = src->xrp(); + } else + { ret = false; + } } }; assign(fees_.base, baseFeeXRP); @@ -608,11 +587,15 @@ Ledger::setup() newFees = baseFeeXRP || reserveBaseXRP || reserveIncrementXRP; } if (oldFees && newFees) + { // Should be all of one or the other, but not both ret = false; + } if (!rules_.enabled(featureXRPFees) && newFees) + { // Can't populate the new fees before the amendment is enabled ret = false; + } } } catch (SHAMapMissingNode const&) @@ -628,20 +611,6 @@ Ledger::setup() return ret; } -void -Ledger::defaultFees(Config const& config) -{ - XRPL_ASSERT( - fees_.base == 0 && fees_.reserve == 0 && fees_.increment == 0, - "xrpl::Ledger::defaultFees : zero fees"); - if (fees_.base == 0) - fees_.base = config.FEES.reference_fee; - if (fees_.reserve == 0) - fees_.reserve = config.FEES.account_reserve; - if (fees_.increment == 0) - fees_.increment = config.FEES.owner_reserve; -} - std::shared_ptr Ledger::peek(Keylet const& k) const { @@ -724,7 +693,7 @@ Ledger::updateNegativeUNL() if (sle->isFieldPresent(sfDisabledValidators)) { auto const& oldNUnl = sle->getFieldArray(sfDisabledValidators); - for (auto v : oldNUnl) + for (auto const& v : oldNUnl) { if (hasToReEnable && v.isFieldPresent(sfPublicKey) && v.getFieldVL(sfPublicKey) == sle->getFieldVL(sfValidatorToReEnable)) @@ -770,9 +739,11 @@ Ledger::walkLedger(beast::Journal j, bool parallel) const else { if (parallel) + { return stateMap_.walkMapParallel(missingNodes1, 32); - else - stateMap_.walkMap(missingNodes1, 32); + } + + stateMap_.walkMap(missingNodes1, 32); } if (!missingNodes1.empty()) @@ -806,27 +777,17 @@ Ledger::walkLedger(beast::Journal j, bool parallel) const } bool -Ledger::assertSensible(beast::Journal ledgerJ) const +Ledger::isSensible() const { - if (header_.hash.isNonZero() && header_.accountHash.isNonZero() && - (header_.accountHash == stateMap_.getHash().as_uint256()) && - (header_.txHash == txMap_.getHash().as_uint256())) - { - return true; - } - - // LCOV_EXCL_START - Json::Value j = getJson({*this, {}}); - - j[jss::accountTreeHash] = to_string(header_.accountHash); - j[jss::transTreeHash] = to_string(header_.txHash); - - JLOG(ledgerJ.fatal()) << "ledger is not sensible" << j; - - UNREACHABLE("xrpl::Ledger::assertSensible : ledger is not sensible"); - - return false; - // LCOV_EXCL_STOP + if (header_.hash.isZero()) + return false; + if (header_.accountHash.isZero()) + return false; + if (header_.accountHash != stateMap_.getHash().as_uint256()) + return false; + if (header_.txHash != txMap_.getHash().as_uint256()) + return false; + return true; } // update the skip list with the information from our previous ledger @@ -837,7 +798,7 @@ Ledger::updateSkipList() if (header_.seq == 0) // genesis ledger has no previous ledger return; - std::uint32_t prevIndex = header_.seq - 1; + std::uint32_t const prevIndex = header_.seq - 1; // update record of every 256th ledger if ((prevIndex & 0xff) == 0) @@ -846,7 +807,7 @@ Ledger::updateSkipList() auto sle = peek(k); std::vector hashes; - bool created; + bool created = false; if (!sle) { sle = std::make_shared(k); @@ -864,16 +825,20 @@ Ledger::updateSkipList() sle->setFieldV256(sfHashes, STVector256(hashes)); sle->setFieldU32(sfLastLedgerSequence, prevIndex); if (created) + { rawInsert(sle); + } else + { rawReplace(sle); + } } // update record of past 256 ledger auto const k = keylet::skip(); auto sle = peek(k); std::vector hashes; - bool created; + bool created = false; if (!sle) { sle = std::make_shared(k); @@ -891,9 +856,13 @@ Ledger::updateSkipList() sle->setFieldV256(sfHashes, STVector256(hashes)); sle->setFieldU32(sfLastLedgerSequence, prevIndex); if (created) + { rawInsert(sle); + } else + { rawReplace(sle); + } } bool @@ -907,76 +876,6 @@ Ledger::isVotingLedger() const return ::xrpl::isVotingLedger(header_.seq + 1); } -static bool -saveValidatedLedger(Application& app, std::shared_ptr const& ledger, bool current) -{ - auto j = app.journal("Ledger"); - auto seq = ledger->header().seq; - if (!app.pendingSaves().startWork(seq)) - { - // The save was completed synchronously - JLOG(j.debug()) << "Save aborted"; - return true; - } - - auto& db = app.getRelationalDatabase(); - - auto const res = db.saveValidatedLedger(ledger, current); - - // Clients can now trust the database for - // information about this ledger sequence. - app.pendingSaves().finishWork(seq); - return res; -} - -/** Save, or arrange to save, a fully-validated ledger - Returns false on error -*/ -bool -pendSaveValidated( - Application& app, - std::shared_ptr const& ledger, - bool isSynchronous, - bool isCurrent) -{ - if (!app.getHashRouter().setFlags(ledger->header().hash, HashRouterFlags::SAVED)) - { - // We have tried to save this ledger recently - auto stream = app.journal("Ledger").debug(); - JLOG(stream) << "Double pend save for " << ledger->header().seq; - - if (!isSynchronous || !app.pendingSaves().pending(ledger->header().seq)) - { - // Either we don't need it to be finished - // or it is finished - return true; - } - } - - XRPL_ASSERT(ledger->isImmutable(), "xrpl::pendSaveValidated : immutable ledger"); - - if (!app.pendingSaves().shouldWork(ledger->header().seq, isSynchronous)) - { - auto stream = app.journal("Ledger").debug(); - JLOG(stream) << "Pend save with seq in pending saves " << ledger->header().seq; - - return true; - } - - // See if we can use the JobQueue. - if (!isSynchronous && - app.getJobQueue().addJob( - isCurrent ? jtPUBLEDGER : jtPUBOLDLEDGER, - std::to_string(ledger->seq()), - [&app, ledger, isCurrent]() { saveValidatedLedger(app, ledger, isCurrent); })) - { - return true; - } - - // The JobQueue won't do the Job. Do the save synchronously. - return saveValidatedLedger(app, ledger, isCurrent); -} - void Ledger::unshare() const { @@ -990,84 +889,5 @@ Ledger::invariants() const stateMap_.invariants(); txMap_.invariants(); } -//------------------------------------------------------------------------------ - -/* - * Make ledger using info loaded from database. - * - * @param LedgerHeader: Ledger information. - * @param app: Link to the Application. - * @param acquire: Acquire the ledger if not found locally. - * @return Shared pointer to the ledger. - */ -std::shared_ptr -loadLedgerHelper(LedgerHeader const& info, Application& app, bool acquire) -{ - bool loaded; - auto ledger = std::make_shared( - info, loaded, acquire, app.config(), app.getNodeFamily(), app.journal("Ledger")); - - if (!loaded) - ledger.reset(); - - return ledger; -} - -static void -finishLoadByIndexOrHash( - std::shared_ptr const& ledger, - Config const& config, - beast::Journal j) -{ - if (!ledger) - return; - - XRPL_ASSERT( - ledger->header().seq < XRP_LEDGER_EARLIEST_FEES || ledger->read(keylet::fees()), - "xrpl::finishLoadByIndexOrHash : valid ledger fees"); - ledger->setImmutable(); - - JLOG(j.trace()) << "Loaded ledger: " << to_string(ledger->header().hash); - - ledger->setFull(); -} - -std::tuple, std::uint32_t, uint256> -getLatestLedger(Application& app) -{ - std::optional const info = app.getRelationalDatabase().getNewestLedgerInfo(); - if (!info) - return {std::shared_ptr(), {}, {}}; - return {loadLedgerHelper(*info, app, true), info->seq, info->hash}; -} - -std::shared_ptr -loadByIndex(std::uint32_t ledgerIndex, Application& app, bool acquire) -{ - if (std::optional info = - app.getRelationalDatabase().getLedgerInfoByIndex(ledgerIndex)) - { - std::shared_ptr ledger = loadLedgerHelper(*info, app, acquire); - finishLoadByIndexOrHash(ledger, app.config(), app.journal("Ledger")); - return ledger; - } - return {}; -} - -std::shared_ptr -loadByHash(uint256 const& ledgerHash, Application& app, bool acquire) -{ - if (std::optional info = - app.getRelationalDatabase().getLedgerInfoByHash(ledgerHash)) - { - std::shared_ptr ledger = loadLedgerHelper(*info, app, acquire); - finishLoadByIndexOrHash(ledger, app.config(), app.journal("Ledger")); - XRPL_ASSERT( - !ledger || ledger->header().hash == ledgerHash, - "xrpl::loadByHash : ledger hash match if loaded"); - return ledger; - } - return {}; -} } // namespace xrpl diff --git a/src/libxrpl/ledger/OpenView.cpp b/src/libxrpl/ledger/OpenView.cpp index 5b94be5da8..b5e358053c 100644 --- a/src/libxrpl/ledger/OpenView.cpp +++ b/src/libxrpl/ledger/OpenView.cpp @@ -185,7 +185,7 @@ OpenView::txsEnd() const -> std::unique_ptr bool OpenView::txExists(key_type const& key) const { - return txs_.find(key) != txs_.end(); + return txs_.contains(key); } auto @@ -198,9 +198,13 @@ OpenView::txRead(key_type const& key) const -> tx_type auto stx = std::make_shared(SerialIter{item.txn->slice()}); decltype(tx_type::second) sto; if (item.meta) + { sto = std::make_shared(SerialIter{item.meta->slice()}, sfMetadata); + } else + { sto = nullptr; + } return {std::move(stx), std::move(sto)}; } diff --git a/src/libxrpl/ledger/PaymentSandbox.cpp b/src/libxrpl/ledger/PaymentSandbox.cpp index e211917466..a56d730b5a 100644 --- a/src/libxrpl/ledger/PaymentSandbox.cpp +++ b/src/libxrpl/ledger/PaymentSandbox.cpp @@ -11,9 +11,11 @@ auto DeferredCredits::makeKey(AccountID const& a1, AccountID const& a2, Currency const& c) -> Key { if (a1 < a2) + { return std::make_tuple(a1, a2, c); - else - return std::make_tuple(a2, a1, c); + } + + return std::make_tuple(a2, a1, c); } void @@ -53,9 +55,13 @@ DeferredCredits::credit( // only record the balance the first time, do not record it here auto& v = i->second; if (sender < receiver) + { v.highAcctCredits += amount; + } else + { v.lowAcctCredits += amount; + } } } @@ -101,11 +107,9 @@ DeferredCredits::adjustments( result.emplace(v.highAcctCredits, v.lowAcctCredits, v.lowAcctOrigBalance); return result; } - else - { - result.emplace(v.lowAcctCredits, v.highAcctCredits, -v.lowAcctOrigBalance); - return result; - } + + result.emplace(v.lowAcctCredits, v.highAcctCredits, -v.lowAcctOrigBalance); + return result; } void @@ -160,7 +164,7 @@ PaymentSandbox::balanceHook( auto delta = amount.zeroed(); auto lastBal = amount; auto minBal = amount; - for (auto curSB = this; curSB; curSB = curSB->ps_) + for (auto curSB = this; curSB != nullptr; curSB = curSB->ps_) { if (auto adj = curSB->tab_.adjustments(account, issuer, currency)) { @@ -179,11 +183,13 @@ PaymentSandbox::balanceHook( adjustedAmt.setIssuer(amount.getIssuer()); if (isXRP(issuer) && adjustedAmt < beast::zero) + { // A calculated negative XRP balance is not an error case. Consider a // payment snippet that credits a large XRP amount and then debits the // same amount. The credit can't be used but we subtract the debit and // calculate a negative value. It's not an error case. adjustedAmt.clear(); + } return adjustedAmt; } @@ -192,7 +198,7 @@ std::uint32_t PaymentSandbox::ownerCountHook(AccountID const& account, std::uint32_t count) const { std::uint32_t result = count; - for (auto curSB = this; curSB; curSB = curSB->ps_) + for (auto curSB = this; curSB != nullptr; curSB = curSB->ps_) { if (auto adj = curSB->tab_.ownerCount(account)) result = std::max(result, *adj); diff --git a/src/libxrpl/ledger/RawStateTable.cpp b/src/libxrpl/ledger/RawStateTable.cpp index fcd8126c87..b411a25b00 100644 --- a/src/libxrpl/ledger/RawStateTable.cpp +++ b/src/libxrpl/ledger/RawStateTable.cpp @@ -94,9 +94,13 @@ public: dereference() const override { if (!sle1_) + { return sle0_; - else if (!sle0_) + } + if (!sle0_) + { return sle1_; + } if (sle1_->key() <= sle0_->key()) return sle1_; return sle0_; @@ -108,9 +112,13 @@ private: { ++iter0_; if (iter0_ == end0_) + { sle0_ = nullptr; + } else + { sle0_ = *iter0_; + } } void @@ -118,9 +126,13 @@ private: { ++iter1_; if (iter1_ == end1_) + { sle1_ = nullptr; + } else + { sle1_ = iter1_->second.sle; + } } void diff --git a/src/libxrpl/ledger/ReadView.cpp b/src/libxrpl/ledger/ReadView.cpp index e0764d6c81..8e6763410c 100644 --- a/src/libxrpl/ledger/ReadView.cpp +++ b/src/libxrpl/ledger/ReadView.cpp @@ -58,7 +58,7 @@ makeRulesGivenLedger( std::unordered_set> const& presets) { Keylet const k = keylet::amendments(); - std::optional digest = ledger.digest(k.key); + std::optional const digest = ledger.digest(k.key); if (digest) { auto const sle = ledger.read(k); diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 7b5b746e0d..1702d4243b 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -2,10 +2,12 @@ #include #include #include -#include -#include #include #include +#include +#include +#include +#include #include #include #include @@ -22,125 +24,6 @@ namespace xrpl { -namespace detail { - -template < - class V, - class N, - class = std::enable_if_t< - std::is_same_v, SLE> && std::is_base_of_v>> -bool -internalDirNext( - V& view, - uint256 const& root, - std::shared_ptr& page, - unsigned int& index, - uint256& entry) -{ - auto const& svIndexes = page->getFieldV256(sfIndexes); - XRPL_ASSERT(index <= svIndexes.size(), "xrpl::detail::internalDirNext : index inside range"); - - if (index >= svIndexes.size()) - { - auto const next = page->getFieldU64(sfIndexNext); - - if (!next) - { - entry.zero(); - return false; - } - - if constexpr (std::is_const_v) - page = view.read(keylet::page(root, next)); - else - page = view.peek(keylet::page(root, next)); - - XRPL_ASSERT(page, "xrpl::detail::internalDirNext : non-null root"); - - if (!page) - return false; - - index = 0; - - return internalDirNext(view, root, page, index, entry); - } - - entry = svIndexes[index++]; - return true; -} - -template < - class V, - class N, - class = std::enable_if_t< - std::is_same_v, SLE> && std::is_base_of_v>> -bool -internalDirFirst( - V& view, - uint256 const& root, - std::shared_ptr& page, - unsigned int& index, - uint256& entry) -{ - if constexpr (std::is_const_v) - page = view.read(keylet::page(root)); - else - page = view.peek(keylet::page(root)); - - if (!page) - return false; - - index = 0; - - return internalDirNext(view, root, page, index, entry); -} - -} // namespace detail - -bool -dirFirst( - ApplyView& view, - uint256 const& root, - std::shared_ptr& page, - unsigned int& index, - uint256& entry) -{ - return detail::internalDirFirst(view, root, page, index, entry); -} - -bool -dirNext( - ApplyView& view, - uint256 const& root, - std::shared_ptr& page, - unsigned int& index, - uint256& entry) -{ - return detail::internalDirNext(view, root, page, index, entry); -} - -bool -cdirFirst( - ReadView const& view, - uint256 const& root, - std::shared_ptr& page, - unsigned int& index, - uint256& entry) -{ - return detail::internalDirFirst(view, root, page, index, entry); -} - -bool -cdirNext( - ReadView const& view, - uint256 const& root, - std::shared_ptr& page, - unsigned int& index, - uint256& entry) -{ - return detail::internalDirNext(view, root, page, index, entry); -} - //------------------------------------------------------------------------------ // // Observers @@ -156,120 +39,6 @@ hasExpired(ReadView const& view, std::optional const& exp) return exp && (view.parentCloseTime() >= tp{d{*exp}}); } -bool -isGlobalFrozen(ReadView const& view, AccountID const& issuer) -{ - if (isXRP(issuer)) - return false; - if (auto const sle = view.read(keylet::account(issuer))) - return sle->isFlag(lsfGlobalFreeze); - return false; -} - -bool -isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue) -{ - if (auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID()))) - return sle->isFlag(lsfMPTLocked); - return false; -} - -bool -isGlobalFrozen(ReadView const& view, Asset const& asset) -{ - return std::visit( - [&](TIss const& issue) { - if constexpr (std::is_same_v) - return isGlobalFrozen(view, issue.getIssuer()); - else - return isGlobalFrozen(view, issue); - }, - asset.value()); -} - -bool -isIndividualFrozen( - ReadView const& view, - AccountID const& account, - Currency const& currency, - AccountID const& issuer) -{ - if (isXRP(currency)) - return false; - if (issuer != account) - { - // Check if the issuer froze the line - auto const sle = view.read(keylet::line(account, issuer, currency)); - if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze)) - return true; - } - return false; -} - -bool -isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue) -{ - if (auto const sle = view.read(keylet::mptoken(mptIssue.getMptID(), account))) - return sle->isFlag(lsfMPTLocked); - return false; -} - -// Can the specified account spend the specified currency issued by -// the specified issuer or does the freeze flag prohibit it? -bool -isFrozen( - ReadView const& view, - AccountID const& account, - Currency const& currency, - AccountID const& issuer) -{ - if (isXRP(currency)) - return false; - auto sle = view.read(keylet::account(issuer)); - if (sle && sle->isFlag(lsfGlobalFreeze)) - return true; - if (issuer != account) - { - // Check if the issuer froze the line - sle = view.read(keylet::line(account, issuer, currency)); - if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze)) - return true; - } - return false; -} - -bool -isFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth) -{ - return isGlobalFrozen(view, mptIssue) || isIndividualFrozen(view, account, mptIssue) || - isVaultPseudoAccountFrozen(view, account, mptIssue, depth); -} - -[[nodiscard]] bool -isAnyFrozen( - ReadView const& view, - std::initializer_list const& accounts, - MPTIssue const& mptIssue, - int depth) -{ - if (isGlobalFrozen(view, mptIssue)) - return true; - - for (auto const& account : accounts) - { - if (isIndividualFrozen(view, account, mptIssue)) - return true; - } - - for (auto const& account : accounts) - { - if (isVaultPseudoAccountFrozen(view, account, mptIssue, depth)) - return true; - } - - return false; -} - bool isVaultPseudoAccountFrozen( ReadView const& view, @@ -311,32 +80,6 @@ isVaultPseudoAccountFrozen( return isAnyFrozen(view, {issuer, account}, vault->at(sfAsset), depth + 1); } -bool -isDeepFrozen( - ReadView const& view, - AccountID const& account, - Currency const& currency, - AccountID const& issuer) -{ - if (isXRP(currency)) - { - return false; - } - - if (issuer == account) - { - return false; - } - - auto const sle = view.read(keylet::line(account, issuer, currency)); - if (!sle) - { - return false; - } - - return sle->isFlag(lsfHighDeepFreeze) || sle->isFlag(lsfLowDeepFreeze); -} - bool isLPTokenFrozen( ReadView const& view, @@ -348,456 +91,6 @@ isLPTokenFrozen( isFrozen(view, account, asset2.currency, asset2.account); } -static SLE::const_pointer -getLineIfUsable( - ReadView const& view, - AccountID const& account, - Currency const& currency, - AccountID const& issuer, - FreezeHandling zeroIfFrozen, - beast::Journal j) -{ - auto const sle = view.read(keylet::line(account, issuer, currency)); - - if (!sle) - { - return nullptr; - } - - if (zeroIfFrozen == fhZERO_IF_FROZEN) - { - if (isFrozen(view, account, currency, issuer) || - isDeepFrozen(view, account, currency, issuer)) - { - return nullptr; - } - - // when fixFrozenLPTokenTransfer is enabled, if currency is lptoken, - // we need to check if the associated assets have been frozen - if (view.rules().enabled(fixFrozenLPTokenTransfer)) - { - auto const sleIssuer = view.read(keylet::account(issuer)); - if (!sleIssuer) - { - return nullptr; // LCOV_EXCL_LINE - } - else if (sleIssuer->isFieldPresent(sfAMMID)) - { - auto const sleAmm = view.read(keylet::amm((*sleIssuer)[sfAMMID])); - - if (!sleAmm || - isLPTokenFrozen( - view, - account, - (*sleAmm)[sfAsset].get(), - (*sleAmm)[sfAsset2].get())) - { - return nullptr; - } - } - } - } - - return sle; -} - -static STAmount -getTrustLineBalance( - ReadView const& view, - SLE::const_ref sle, - AccountID const& account, - Currency const& currency, - AccountID const& issuer, - bool includeOppositeLimit, - beast::Journal j) -{ - STAmount amount; - if (sle) - { - amount = sle->getFieldAmount(sfBalance); - bool const accountHigh = account > issuer; - auto const& oppositeField = accountHigh ? sfLowLimit : sfHighLimit; - if (accountHigh) - { - // Put balance in account terms. - amount.negate(); - } - if (includeOppositeLimit) - { - amount += sle->getFieldAmount(oppositeField); - } - amount.setIssuer(issuer); - } - else - { - amount.clear(Issue{currency, issuer}); - } - - JLOG(j.trace()) << "getTrustLineBalance:" << " account=" << to_string(account) - << " amount=" << amount.getFullText(); - - return view.balanceHook(account, issuer, amount); -} - -STAmount -accountHolds( - ReadView const& view, - AccountID const& account, - Currency const& currency, - AccountID const& issuer, - FreezeHandling zeroIfFrozen, - beast::Journal j, - SpendableHandling includeFullBalance) -{ - STAmount amount; - if (isXRP(currency)) - { - return {xrpLiquid(view, account, 0, j)}; - } - - bool const returnSpendable = (includeFullBalance == shFULL_BALANCE); - if (returnSpendable && account == issuer) - // If the account is the issuer, then their limit is effectively - // infinite - return STAmount{Issue{currency, issuer}, STAmount::cMaxValue, STAmount::cMaxOffset}; - - // IOU: Return balance on trust line modulo freeze - SLE::const_pointer const sle = - getLineIfUsable(view, account, currency, issuer, zeroIfFrozen, j); - - return getTrustLineBalance(view, sle, account, currency, issuer, returnSpendable, j); -} - -STAmount -accountHolds( - ReadView const& view, - AccountID const& account, - Issue const& issue, - FreezeHandling zeroIfFrozen, - beast::Journal j, - SpendableHandling includeFullBalance) -{ - return accountHolds( - view, account, issue.currency, issue.account, zeroIfFrozen, j, includeFullBalance); -} - -STAmount -accountHolds( - ReadView const& view, - AccountID const& account, - MPTIssue const& mptIssue, - FreezeHandling zeroIfFrozen, - AuthHandling zeroIfUnauthorized, - beast::Journal j, - SpendableHandling includeFullBalance) -{ - bool const returnSpendable = (includeFullBalance == shFULL_BALANCE); - - if (returnSpendable && account == mptIssue.getIssuer()) - { - // if the account is the issuer, and the issuance exists, their limit is - // the issuance limit minus the outstanding value - auto const issuance = view.read(keylet::mptIssuance(mptIssue.getMptID())); - - if (!issuance) - { - return STAmount{mptIssue}; - } - return STAmount{ - mptIssue, - issuance->at(~sfMaximumAmount).value_or(maxMPTokenAmount) - - issuance->at(sfOutstandingAmount)}; - } - - STAmount amount; - - auto const sleMpt = view.read(keylet::mptoken(mptIssue.getMptID(), account)); - - if (!sleMpt) - amount.clear(mptIssue); - else if (zeroIfFrozen == fhZERO_IF_FROZEN && isFrozen(view, account, mptIssue)) - amount.clear(mptIssue); - else - { - amount = STAmount{mptIssue, sleMpt->getFieldU64(sfMPTAmount)}; - - // Only if auth check is needed, as it needs to do an additional read - // operation. Note featureSingleAssetVault will affect error codes. - if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED && - view.rules().enabled(featureSingleAssetVault)) - { - if (auto const err = requireAuth(view, mptIssue, account, AuthType::StrongAuth); - !isTesSuccess(err)) - amount.clear(mptIssue); - } - else if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED) - { - auto const sleIssuance = view.read(keylet::mptIssuance(mptIssue.getMptID())); - - // if auth is enabled on the issuance and mpt is not authorized, - // clear amount - if (sleIssuance && sleIssuance->isFlag(lsfMPTRequireAuth) && - !sleMpt->isFlag(lsfMPTAuthorized)) - amount.clear(mptIssue); - } - } - - return amount; -} - -[[nodiscard]] STAmount -accountHolds( - ReadView const& view, - AccountID const& account, - Asset const& asset, - FreezeHandling zeroIfFrozen, - AuthHandling zeroIfUnauthorized, - beast::Journal j, - SpendableHandling includeFullBalance) -{ - return std::visit( - [&](TIss const& value) { - if constexpr (std::is_same_v) - { - return accountHolds(view, account, value, zeroIfFrozen, j, includeFullBalance); - } - else if constexpr (std::is_same_v) - { - return accountHolds( - view, account, value, zeroIfFrozen, zeroIfUnauthorized, j, includeFullBalance); - } - }, - asset.value()); -} - -STAmount -accountFunds( - ReadView const& view, - AccountID const& id, - STAmount const& saDefault, - FreezeHandling freezeHandling, - beast::Journal j) -{ - if (!saDefault.native() && saDefault.getIssuer() == id) - return saDefault; - - return accountHolds( - view, id, saDefault.getCurrency(), saDefault.getIssuer(), freezeHandling, j); -} - -// Prevent ownerCount from wrapping under error conditions. -// -// adjustment allows the ownerCount to be adjusted up or down in multiple steps. -// If id != std::nullopt, then do error reporting. -// -// Returns adjusted owner count. -static std::uint32_t -confineOwnerCount( - std::uint32_t current, - std::int32_t adjustment, - std::optional const& id = std::nullopt, - beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) -{ - std::uint32_t adjusted{current + adjustment}; - if (adjustment > 0) - { - // Overflow is well defined on unsigned - if (adjusted < current) - { - if (id) - { - JLOG(j.fatal()) << "Account " << *id << " owner count exceeds max!"; - } - adjusted = std::numeric_limits::max(); - } - } - else - { - // Underflow is well defined on unsigned - if (adjusted > current) - { - if (id) - { - JLOG(j.fatal()) << "Account " << *id << " owner count set below 0!"; - } - adjusted = 0; - XRPL_ASSERT(!id, "xrpl::confineOwnerCount : id is not set"); - } - } - return adjusted; -} - -XRPAmount -xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j) -{ - auto const sle = view.read(keylet::account(id)); - if (sle == nullptr) - return beast::zero; - - // Return balance minus reserve - std::uint32_t const ownerCount = - confineOwnerCount(view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj); - - // Pseudo-accounts have no reserve requirement - auto const reserve = - isPseudoAccount(sle) ? XRPAmount{0} : view.fees().accountReserve(ownerCount); - - auto const fullBalance = sle->getFieldAmount(sfBalance); - - auto const balance = view.balanceHook(id, xrpAccount(), fullBalance); - - STAmount const amount = (balance < reserve) ? STAmount{0} : balance - reserve; - - JLOG(j.trace()) << "accountHolds:" << " account=" << to_string(id) - << " amount=" << amount.getFullText() - << " fullBalance=" << fullBalance.getFullText() - << " balance=" << balance.getFullText() << " reserve=" << reserve - << " ownerCount=" << ownerCount << " ownerCountAdj=" << ownerCountAdj; - - return amount.xrp(); -} - -void -forEachItem( - ReadView const& view, - Keylet const& root, - std::function const&)> const& f) -{ - XRPL_ASSERT(root.type == ltDIR_NODE, "xrpl::forEachItem : valid root type"); - - if (root.type != ltDIR_NODE) - return; - - auto pos = root; - - while (true) - { - auto sle = view.read(pos); - if (!sle) - return; - for (auto const& key : sle->getFieldV256(sfIndexes)) - f(view.read(keylet::child(key))); - auto const next = sle->getFieldU64(sfIndexNext); - if (!next) - return; - pos = keylet::page(root, next); - } -} - -bool -forEachItemAfter( - ReadView const& view, - Keylet const& root, - uint256 const& after, - std::uint64_t const hint, - unsigned int limit, - std::function const&)> const& f) -{ - XRPL_ASSERT(root.type == ltDIR_NODE, "xrpl::forEachItemAfter : valid root type"); - - if (root.type != ltDIR_NODE) - return false; - - auto currentIndex = root; - - // If startAfter is not zero try jumping to that page using the hint - if (after.isNonZero()) - { - auto const hintIndex = keylet::page(root, hint); - - if (auto hintDir = view.read(hintIndex)) - { - for (auto const& key : hintDir->getFieldV256(sfIndexes)) - { - if (key == after) - { - // We found the hint, we can start here - currentIndex = hintIndex; - break; - } - } - } - - bool found = false; - for (;;) - { - auto const ownerDir = view.read(currentIndex); - if (!ownerDir) - return found; - for (auto const& key : ownerDir->getFieldV256(sfIndexes)) - { - if (!found) - { - if (key == after) - found = true; - } - else if (f(view.read(keylet::child(key))) && limit-- <= 1) - { - return found; - } - } - - auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext); - if (uNodeNext == 0) - return found; - currentIndex = keylet::page(root, uNodeNext); - } - } - else - { - for (;;) - { - auto const ownerDir = view.read(currentIndex); - if (!ownerDir) - return true; - for (auto const& key : ownerDir->getFieldV256(sfIndexes)) - if (f(view.read(keylet::child(key))) && limit-- <= 1) - return true; - auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext); - if (uNodeNext == 0) - return true; - currentIndex = keylet::page(root, uNodeNext); - } - } -} - -Rate -transferRate(ReadView const& view, AccountID const& issuer) -{ - auto const sle = view.read(keylet::account(issuer)); - - if (sle && sle->isFieldPresent(sfTransferRate)) - return Rate{sle->getFieldU32(sfTransferRate)}; - - return parityRate; -} - -Rate -transferRate(ReadView const& view, MPTID const& issuanceID) -{ - // fee is 0-50,000 (0-50%), rate is 1,000,000,000-2,000,000,000 - // For example, if transfer fee is 50% then 10,000 * 50,000 = 500,000 - // which represents 50% of 1,000,000,000 - if (auto const sle = view.read(keylet::mptIssuance(issuanceID)); - sle && sle->isFieldPresent(sfTransferFee)) - return Rate{1'000'000'000u + 10'000 * sle->getFieldU16(sfTransferFee)}; - - return parityRate; -} - -Rate -transferRate(ReadView const& view, STAmount const& amount) -{ - return std::visit( - [&](TIss const& issue) { - if constexpr (std::is_same_v) - return transferRate(view, issue.getIssuer()); - else - return transferRate(view, issue.getMptID()); - }, - amount.asset().value()); -} - bool areCompatible( ReadView const& validLedger, @@ -896,20 +189,6 @@ areCompatible( return ret; } -bool -dirIsEmpty(ReadView const& view, Keylet const& k) -{ - auto const sleNode = view.read(k); - if (!sleNode) - return true; - if (!sleNode->getFieldV256(sfIndexes).empty()) - return false; - // The first page of a directory may legitimately be empty even if there - // are other pages (the first page is the anchor page) so check to see if - // there is another page. If there is, the directory isn't empty. - return sleNode->getFieldU64(sfIndexNext) == 0; -} - std::set getEnabledAmendments(ReadView const& view) { @@ -963,7 +242,7 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal) if (seq == (ledger.seq() - 1)) return ledger.header().parentHash; - if (int diff = ledger.seq() - seq; diff <= 256) + if (int const diff = ledger.seq() - seq; diff <= 256) { // Within 256... auto const hashIndex = ledger.read(keylet::skip()); @@ -1013,30 +292,6 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal) // //------------------------------------------------------------------------------ -void -adjustOwnerCount( - ApplyView& view, - std::shared_ptr const& sle, - std::int32_t amount, - beast::Journal j) -{ - if (!sle) - return; - XRPL_ASSERT(amount, "xrpl::adjustOwnerCount : nonzero amount input"); - std::uint32_t const current{sle->getFieldU32(sfOwnerCount)}; - AccountID const id = (*sle)[sfAccount]; - std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j); - view.adjustOwnerCountHook(id, current, adjusted); - sle->at(sfOwnerCount) = adjusted; - view.update(sle); -} - -std::function -describeOwnerDir(AccountID const& account) -{ - return [account](std::shared_ptr const& sle) { (*sle)[sfOwner] = account; }; -} - TER dirLink( ApplyView& view, @@ -1052,164 +307,6 @@ dirLink( return tesSUCCESS; } -AccountID -pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey) -{ - // This number must not be changed without an amendment - constexpr std::uint16_t maxAccountAttempts = 256; - for (std::uint16_t i = 0; i < maxAccountAttempts; ++i) - { - ripesha_hasher rsh; - auto const hash = sha512Half(i, view.header().parentHash, pseudoOwnerKey); - rsh(hash.data(), hash.size()); - AccountID const ret{static_cast(rsh)}; - if (!view.read(keylet::account(ret))) - return ret; - } - return beast::zero; -} - -// Pseudo-account designator fields MUST be maintained by including the -// SField::sMD_PseudoAccount flag in the SField definition. (Don't forget to -// "| SField::sMD_Default"!) The fields do NOT need to be amendment-gated, -// since a non-active amendment will not set any field, by definition. -// Specific properties of a pseudo-account are NOT checked here, that's what -// InvariantCheck is for. -[[nodiscard]] std::vector const& -getPseudoAccountFields() -{ - static std::vector const pseudoFields = []() { - auto const ar = LedgerFormats::getInstance().findByType(ltACCOUNT_ROOT); - if (!ar) - { - // LCOV_EXCL_START - LogicError( - "xrpl::getPseudoAccountFields : unable to find account root " - "ledger format"); - // LCOV_EXCL_STOP - } - auto const& soTemplate = ar->getSOTemplate(); - - std::vector pseudoFields; - for (auto const& field : soTemplate) - { - if (field.sField().shouldMeta(SField::sMD_PseudoAccount)) - pseudoFields.emplace_back(&field.sField()); - } - return pseudoFields; - }(); - return pseudoFields; -} - -[[nodiscard]] bool -isPseudoAccount( - std::shared_ptr sleAcct, - std::set const& pseudoFieldFilter) -{ - auto const& fields = getPseudoAccountFields(); - - // Intentionally use defensive coding here because it's cheap and makes the - // semantics of true return value clean. - return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT && - std::count_if( - fields.begin(), fields.end(), [&sleAcct, &pseudoFieldFilter](SField const* sf) -> bool { - return sleAcct->isFieldPresent(*sf) && - (pseudoFieldFilter.empty() || pseudoFieldFilter.contains(sf)); - }) > 0; -} - -Expected, TER> -createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField) -{ - [[maybe_unused]] - auto const& fields = getPseudoAccountFields(); - XRPL_ASSERT( - std::count_if( - fields.begin(), - fields.end(), - [&ownerField](SField const* sf) -> bool { return *sf == ownerField; }) == 1, - "xrpl::createPseudoAccount : valid owner field"); - - auto const accountId = pseudoAccountAddress(view, pseudoOwnerKey); - if (accountId == beast::zero) - return Unexpected(tecDUPLICATE); - - // Create pseudo-account. - auto account = std::make_shared(keylet::account(accountId)); - account->setAccountID(sfAccount, accountId); - account->setFieldAmount(sfBalance, STAmount{}); - - // Pseudo-accounts can't submit transactions, so set the sequence number - // to 0 to make them easier to spot and verify, and add an extra level - // of protection. - std::uint32_t const seqno = // - view.rules().enabled(featureSingleAssetVault) || // - view.rules().enabled(featureLendingProtocol) // - ? 0 // - : view.seq(); - account->setFieldU32(sfSequence, seqno); - // Ignore reserves requirement, disable the master key, allow default - // rippling, and enable deposit authorization to prevent payments into - // pseudo-account. - account->setFieldU32(sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth); - // Link the pseudo-account with its owner object. - account->setFieldH256(ownerField, pseudoOwnerKey); - - view.insert(account); - - return account; -} - -[[nodiscard]] TER -canAddHolding(ReadView const& view, Issue const& issue) -{ - if (issue.native()) - return tesSUCCESS; // No special checks for XRP - - auto const issuer = view.read(keylet::account(issue.getIssuer())); - if (!issuer) - return terNO_ACCOUNT; - else if (!issuer->isFlag(lsfDefaultRipple)) - return terNO_RIPPLE; - - return tesSUCCESS; -} - -[[nodiscard]] TER -canAddHolding(ReadView const& view, MPTIssue const& mptIssue) -{ - auto mptID = mptIssue.getMptID(); - auto issuance = view.read(keylet::mptIssuance(mptID)); - if (!issuance) - return tecOBJECT_NOT_FOUND; - if (!issuance->isFlag(lsfMPTCanTransfer)) - return tecNO_AUTH; - - return tesSUCCESS; -} - -[[nodiscard]] TER -canAddHolding(ReadView const& view, Asset const& asset) -{ - return std::visit( - [&](TIss const& issue) -> TER { return canAddHolding(view, issue); }, - asset.value()); -} - -[[nodiscard]] TER -checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag) -{ - if (toSle == nullptr) - return tecNO_DST; - - // The tag is basically account-specific information we don't - // understand, but we can require someone to fill it in. - if (toSle->isFlag(lsfRequireDestTag) && !hasDestinationTag) - return tecDST_TAG_NEEDED; // Cannot send without a tag - - return tesSUCCESS; -} - /* * Checks if a withdrawal amount into the destination account exceeds * any applicable receiving limit. @@ -1321,7 +418,7 @@ doWithdraw( } else { - auto dstSle = view.peek(keylet::account(dstAcct)); + auto dstSle = view.read(keylet::account(dstAcct)); if (auto err = verifyDepositPreauth(tx, view, senderAcct, dstAcct, dstSle, j)) return err; } @@ -1336,8 +433,7 @@ doWithdraw( j) < amount) { // LCOV_EXCL_START - JLOG(j.error()) << "LoanBrokerCoverWithdraw: negative balance of " - "broker cover assets."; + JLOG(j.error()) << "doWithdraw: negative balance of broker cover assets."; return tefINTERNAL; // LCOV_EXCL_STOP } @@ -1347,1809 +443,6 @@ doWithdraw( return accountSend(view, sourceAcct, dstAcct, amount, j, WaiveTransferFee::Yes); } -[[nodiscard]] TER -addEmptyHolding( - ApplyView& view, - AccountID const& accountID, - XRPAmount priorBalance, - Issue const& issue, - beast::Journal journal) -{ - // Every account can hold XRP. An issuer can issue directly. - if (issue.native() || accountID == issue.getIssuer()) - return tesSUCCESS; - - auto const& issuerId = issue.getIssuer(); - auto const& currency = issue.currency; - if (isGlobalFrozen(view, issuerId)) - return tecFROZEN; // LCOV_EXCL_LINE - - auto const& srcId = issuerId; - auto const& dstId = accountID; - auto const high = srcId > dstId; - auto const index = keylet::line(srcId, dstId, currency); - auto const sleSrc = view.peek(keylet::account(srcId)); - auto const sleDst = view.peek(keylet::account(dstId)); - if (!sleDst || !sleSrc) - return tefINTERNAL; // LCOV_EXCL_LINE - if (!sleSrc->isFlag(lsfDefaultRipple)) - return tecINTERNAL; // LCOV_EXCL_LINE - // If the line already exists, don't create it again. - if (view.read(index)) - return tecDUPLICATE; - - // Can the account cover the trust line reserve ? - std::uint32_t const ownerCount = sleDst->at(sfOwnerCount); - if (priorBalance < view.fees().accountReserve(ownerCount + 1)) - return tecNO_LINE_INSUF_RESERVE; - - return trustCreate( - view, - high, - srcId, - dstId, - index.key, - sleDst, - /*bAuth=*/false, - /*bNoRipple=*/true, - /*bFreeze=*/false, - /*deepFreeze*/ false, - /*saBalance=*/STAmount{Issue{currency, noAccount()}}, - /*saLimit=*/STAmount{Issue{currency, dstId}}, - /*uSrcQualityIn=*/0, - /*uSrcQualityOut=*/0, - journal); -} - -[[nodiscard]] TER -addEmptyHolding( - ApplyView& view, - AccountID const& accountID, - XRPAmount priorBalance, - MPTIssue const& mptIssue, - beast::Journal journal) -{ - auto const& mptID = mptIssue.getMptID(); - auto const mpt = view.peek(keylet::mptIssuance(mptID)); - if (!mpt) - return tefINTERNAL; // LCOV_EXCL_LINE - if (mpt->isFlag(lsfMPTLocked)) - return tefINTERNAL; // LCOV_EXCL_LINE - if (view.peek(keylet::mptoken(mptID, accountID))) - return tecDUPLICATE; - if (accountID == mptIssue.getIssuer()) - return tesSUCCESS; - - return authorizeMPToken(view, priorBalance, mptID, accountID, journal); -} - -[[nodiscard]] TER -authorizeMPToken( - ApplyView& view, - XRPAmount const& priorBalance, - MPTID const& mptIssuanceID, - AccountID const& account, - beast::Journal journal, - std::uint32_t flags, - std::optional holderID) -{ - auto const sleAcct = view.peek(keylet::account(account)); - if (!sleAcct) - return tecINTERNAL; // LCOV_EXCL_LINE - - // If the account that submitted the tx is a holder - // Note: `account_` is holder's account - // `holderID` is NOT used - if (!holderID) - { - // When a holder wants to unauthorize/delete a MPT, the ledger must - // - delete mptokenKey from owner directory - // - delete the MPToken - if (flags & tfMPTUnauthorize) - { - auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); - auto const sleMpt = view.peek(mptokenKey); - if (!sleMpt || (*sleMpt)[sfMPTAmount] != 0) - return tecINTERNAL; // LCOV_EXCL_LINE - - if (!view.dirRemove( - keylet::ownerDir(account), (*sleMpt)[sfOwnerNode], sleMpt->key(), false)) - return tecINTERNAL; // LCOV_EXCL_LINE - - adjustOwnerCount(view, sleAcct, -1, journal); - - view.erase(sleMpt); - return tesSUCCESS; - } - - // A potential holder wants to authorize/hold a mpt, the ledger must: - // - add the new mptokenKey to the owner directory - // - create the MPToken object for the holder - - // The reserve that is required to create the MPToken. Note - // that although the reserve increases with every item - // an account owns, in the case of MPTokens we only - // *enforce* a reserve if the user owns more than two - // items. This is similar to the reserve requirements of trust lines. - std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount); - XRPAmount const reserveCreate( - (uOwnerCount < 2) ? XRPAmount(beast::zero) - : view.fees().accountReserve(uOwnerCount + 1)); - - if (priorBalance < reserveCreate) - return tecINSUFFICIENT_RESERVE; - - // Defensive check before we attempt to create MPToken for the issuer - auto const mpt = view.read(keylet::mptIssuance(mptIssuanceID)); - if (!mpt || mpt->getAccountID(sfIssuer) == account) - { - // LCOV_EXCL_START - UNREACHABLE("xrpl::authorizeMPToken : invalid issuance or issuers token"); - if (view.rules().enabled(featureLendingProtocol)) - return tecINTERNAL; - // LCOV_EXCL_STOP - } - - auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); - auto mptoken = std::make_shared(mptokenKey); - if (auto ter = dirLink(view, account, mptoken)) - return ter; // LCOV_EXCL_LINE - - (*mptoken)[sfAccount] = account; - (*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID; - (*mptoken)[sfFlags] = 0; - view.insert(mptoken); - - // Update owner count. - adjustOwnerCount(view, sleAcct, 1, journal); - - return tesSUCCESS; - } - - auto const sleMptIssuance = view.read(keylet::mptIssuance(mptIssuanceID)); - if (!sleMptIssuance) - return tecINTERNAL; // LCOV_EXCL_LINE - - // If the account that submitted this tx is the issuer of the MPT - // Note: `account_` is issuer's account - // `holderID` is holder's account - if (account != (*sleMptIssuance)[sfIssuer]) - return tecINTERNAL; // LCOV_EXCL_LINE - - auto const sleMpt = view.peek(keylet::mptoken(mptIssuanceID, *holderID)); - if (!sleMpt) - return tecINTERNAL; // LCOV_EXCL_LINE - - std::uint32_t const flagsIn = sleMpt->getFieldU32(sfFlags); - std::uint32_t flagsOut = flagsIn; - - // Issuer wants to unauthorize the holder, unset lsfMPTAuthorized on - // their MPToken - if (flags & tfMPTUnauthorize) - flagsOut &= ~lsfMPTAuthorized; - // Issuer wants to authorize a holder, set lsfMPTAuthorized on their - // MPToken - else - flagsOut |= lsfMPTAuthorized; - - if (flagsIn != flagsOut) - sleMpt->setFieldU32(sfFlags, flagsOut); - - view.update(sleMpt); - return tesSUCCESS; -} - -TER -trustCreate( - ApplyView& view, - bool const bSrcHigh, - AccountID const& uSrcAccountID, - AccountID const& uDstAccountID, - uint256 const& uIndex, // --> ripple state entry - SLE::ref sleAccount, // --> the account being set. - bool const bAuth, // --> authorize account. - bool const bNoRipple, // --> others cannot ripple through - bool const bFreeze, // --> funds cannot leave - bool bDeepFreeze, // --> can neither receive nor send funds - STAmount const& saBalance, // --> balance of account being set. - // Issuer should be noAccount() - STAmount const& saLimit, // --> limit for account being set. - // Issuer should be the account being set. - std::uint32_t uQualityIn, - std::uint32_t uQualityOut, - beast::Journal j) -{ - JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", " - << to_string(uDstAccountID) << ", " << saBalance.getFullText(); - - auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID; - auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID; - if (uLowAccountID == uHighAccountID) - { - // LCOV_EXCL_START - UNREACHABLE("xrpl::trustCreate : trust line to self"); - if (view.rules().enabled(featureLendingProtocol)) - return tecINTERNAL; - // LCOV_EXCL_STOP - } - - auto const sleRippleState = std::make_shared(ltRIPPLE_STATE, uIndex); - view.insert(sleRippleState); - - auto lowNode = view.dirInsert( - keylet::ownerDir(uLowAccountID), sleRippleState->key(), describeOwnerDir(uLowAccountID)); - - if (!lowNode) - return tecDIR_FULL; // LCOV_EXCL_LINE - - auto highNode = view.dirInsert( - keylet::ownerDir(uHighAccountID), sleRippleState->key(), describeOwnerDir(uHighAccountID)); - - if (!highNode) - return tecDIR_FULL; // LCOV_EXCL_LINE - - bool const bSetDst = saLimit.getIssuer() == uDstAccountID; - bool const bSetHigh = bSrcHigh ^ bSetDst; - - XRPL_ASSERT(sleAccount, "xrpl::trustCreate : non-null SLE"); - if (!sleAccount) - return tefINTERNAL; // LCOV_EXCL_LINE - - XRPL_ASSERT( - sleAccount->getAccountID(sfAccount) == (bSetHigh ? uHighAccountID : uLowAccountID), - "xrpl::trustCreate : matching account ID"); - auto const slePeer = view.peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID)); - if (!slePeer) - return tecNO_TARGET; - - // Remember deletion hints. - sleRippleState->setFieldU64(sfLowNode, *lowNode); - sleRippleState->setFieldU64(sfHighNode, *highNode); - - sleRippleState->setFieldAmount(bSetHigh ? sfHighLimit : sfLowLimit, saLimit); - sleRippleState->setFieldAmount( - bSetHigh ? sfLowLimit : sfHighLimit, - STAmount(Issue{saBalance.getCurrency(), bSetDst ? uSrcAccountID : uDstAccountID})); - - if (uQualityIn) - sleRippleState->setFieldU32(bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn); - - if (uQualityOut) - sleRippleState->setFieldU32(bSetHigh ? sfHighQualityOut : sfLowQualityOut, uQualityOut); - - std::uint32_t uFlags = bSetHigh ? lsfHighReserve : lsfLowReserve; - - if (bAuth) - { - uFlags |= (bSetHigh ? lsfHighAuth : lsfLowAuth); - } - if (bNoRipple) - { - uFlags |= (bSetHigh ? lsfHighNoRipple : lsfLowNoRipple); - } - if (bFreeze) - { - uFlags |= (bSetHigh ? lsfHighFreeze : lsfLowFreeze); - } - if (bDeepFreeze) - { - uFlags |= (bSetHigh ? lsfHighDeepFreeze : lsfLowDeepFreeze); - } - - if ((slePeer->getFlags() & lsfDefaultRipple) == 0) - { - // The other side's default is no rippling - uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple); - } - - sleRippleState->setFieldU32(sfFlags, uFlags); - adjustOwnerCount(view, sleAccount, 1, j); - - // ONLY: Create ripple balance. - sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance); - - view.creditHook(uSrcAccountID, uDstAccountID, saBalance, saBalance.zeroed()); - - return tesSUCCESS; -} - -[[nodiscard]] TER -removeEmptyHolding( - ApplyView& view, - AccountID const& accountID, - Issue const& issue, - beast::Journal journal) -{ - if (issue.native()) - { - auto const sle = view.read(keylet::account(accountID)); - if (!sle) - return tecINTERNAL; // LCOV_EXCL_LINE - - auto const balance = sle->getFieldAmount(sfBalance); - if (balance.xrp() != 0) - return tecHAS_OBLIGATIONS; - - return tesSUCCESS; - } - - // `asset` is an IOU. - // If the account is the issuer, then no line should exist. Check anyway. If - // a line does exist, it will get deleted. If not, return success. - bool const accountIsIssuer = accountID == issue.account; - auto const line = view.peek(keylet::line(accountID, issue)); - if (!line) - return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND; - if (!accountIsIssuer && line->at(sfBalance)->iou() != beast::zero) - return tecHAS_OBLIGATIONS; - - // Adjust the owner count(s) - if (line->isFlag(lsfLowReserve)) - { - // Clear reserve for low account. - auto sleLowAccount = view.peek(keylet::account(line->at(sfLowLimit)->getIssuer())); - if (!sleLowAccount) - return tecINTERNAL; // LCOV_EXCL_LINE - - adjustOwnerCount(view, sleLowAccount, -1, journal); - // It's not really necessary to clear the reserve flag, since the line - // is about to be deleted, but this will make the metadata reflect an - // accurate state at the time of deletion. - line->clearFlag(lsfLowReserve); - } - - if (line->isFlag(lsfHighReserve)) - { - // Clear reserve for high account. - auto sleHighAccount = view.peek(keylet::account(line->at(sfHighLimit)->getIssuer())); - if (!sleHighAccount) - return tecINTERNAL; // LCOV_EXCL_LINE - - adjustOwnerCount(view, sleHighAccount, -1, journal); - // It's not really necessary to clear the reserve flag, since the line - // is about to be deleted, but this will make the metadata reflect an - // accurate state at the time of deletion. - line->clearFlag(lsfHighReserve); - } - - return trustDelete( - view, line, line->at(sfLowLimit)->getIssuer(), line->at(sfHighLimit)->getIssuer(), journal); -} - -[[nodiscard]] TER -removeEmptyHolding( - ApplyView& view, - AccountID const& accountID, - MPTIssue const& mptIssue, - beast::Journal journal) -{ - // If the account is the issuer, then no token should exist. MPTs do not - // have the legacy ability to create such a situation, but check anyway. If - // a token does exist, it will get deleted. If not, return success. - bool const accountIsIssuer = accountID == mptIssue.getIssuer(); - auto const& mptID = mptIssue.getMptID(); - auto const mptoken = view.peek(keylet::mptoken(mptID, accountID)); - if (!mptoken) - return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND; - // Unlike a trust line, if the account is the issuer, and the token has a - // balance, it can not just be deleted, because that will throw the issuance - // accounting out of balance, so fail. Since this should be impossible - // anyway, I'm not going to put any effort into it. - if (mptoken->at(sfMPTAmount) != 0) - return tecHAS_OBLIGATIONS; - - return authorizeMPToken( - view, - {}, // priorBalance - mptID, - accountID, - journal, - tfMPTUnauthorize // flags - ); -} - -TER -trustDelete( - ApplyView& view, - std::shared_ptr const& sleRippleState, - AccountID const& uLowAccountID, - AccountID const& uHighAccountID, - beast::Journal j) -{ - // Detect legacy dirs. - std::uint64_t uLowNode = sleRippleState->getFieldU64(sfLowNode); - std::uint64_t uHighNode = sleRippleState->getFieldU64(sfHighNode); - - JLOG(j.trace()) << "trustDelete: Deleting ripple line: low"; - - if (!view.dirRemove(keylet::ownerDir(uLowAccountID), uLowNode, sleRippleState->key(), false)) - { - return tefBAD_LEDGER; // LCOV_EXCL_LINE - } - - JLOG(j.trace()) << "trustDelete: Deleting ripple line: high"; - - if (!view.dirRemove(keylet::ownerDir(uHighAccountID), uHighNode, sleRippleState->key(), false)) - { - return tefBAD_LEDGER; // LCOV_EXCL_LINE - } - - JLOG(j.trace()) << "trustDelete: Deleting ripple line: state"; - view.erase(sleRippleState); - - return tesSUCCESS; -} - -TER -offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j) -{ - if (!sle) - return tesSUCCESS; - auto offerIndex = sle->key(); - auto owner = sle->getAccountID(sfAccount); - - // Detect legacy directories. - uint256 uDirectory = sle->getFieldH256(sfBookDirectory); - - if (!view.dirRemove(keylet::ownerDir(owner), sle->getFieldU64(sfOwnerNode), offerIndex, false)) - { - return tefBAD_LEDGER; // LCOV_EXCL_LINE - } - - if (!view.dirRemove(keylet::page(uDirectory), sle->getFieldU64(sfBookNode), offerIndex, false)) - { - return tefBAD_LEDGER; // LCOV_EXCL_LINE - } - - if (sle->isFieldPresent(sfAdditionalBooks)) - { - XRPL_ASSERT( - sle->isFlag(lsfHybrid) && sle->isFieldPresent(sfDomainID), - "xrpl::offerDelete : should be a hybrid domain offer"); - - auto const& additionalBookDirs = sle->getFieldArray(sfAdditionalBooks); - - for (auto const& bookDir : additionalBookDirs) - { - auto const& dirIndex = bookDir.getFieldH256(sfBookDirectory); - auto const& dirNode = bookDir.getFieldU64(sfBookNode); - - if (!view.dirRemove(keylet::page(dirIndex), dirNode, offerIndex, false)) - { - return tefBAD_LEDGER; // LCOV_EXCL_LINE - } - } - } - - adjustOwnerCount(view, view.peek(keylet::account(owner)), -1, j); - - view.erase(sle); - - return tesSUCCESS; -} - -// Direct send w/o fees: -// - Redeeming IOUs and/or sending sender's own IOUs. -// - Create trust line if needed. -// --> bCheckIssuer : normally require issuer to be involved. -static TER -rippleCreditIOU( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - bool bCheckIssuer, - beast::Journal j) -{ - AccountID const& issuer = saAmount.getIssuer(); - Currency const& currency = saAmount.getCurrency(); - - // Make sure issuer is involved. - XRPL_ASSERT( - !bCheckIssuer || uSenderID == issuer || uReceiverID == issuer, - "xrpl::rippleCreditIOU : matching issuer or don't care"); - (void)issuer; - - // Disallow sending to self. - XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::rippleCreditIOU : sender is not receiver"); - - bool const bSenderHigh = uSenderID > uReceiverID; - auto const index = keylet::line(uSenderID, uReceiverID, currency); - - XRPL_ASSERT( - !isXRP(uSenderID) && uSenderID != noAccount(), "xrpl::rippleCreditIOU : sender is not XRP"); - XRPL_ASSERT( - !isXRP(uReceiverID) && uReceiverID != noAccount(), - "xrpl::rippleCreditIOU : receiver is not XRP"); - - // If the line exists, modify it accordingly. - if (auto const sleRippleState = view.peek(index)) - { - STAmount saBalance = sleRippleState->getFieldAmount(sfBalance); - - if (bSenderHigh) - saBalance.negate(); // Put balance in sender terms. - - view.creditHook(uSenderID, uReceiverID, saAmount, saBalance); - - STAmount const saBefore = saBalance; - - saBalance -= saAmount; - - JLOG(j.trace()) << "rippleCreditIOU: " << to_string(uSenderID) << " -> " - << to_string(uReceiverID) << " : before=" << saBefore.getFullText() - << " amount=" << saAmount.getFullText() - << " after=" << saBalance.getFullText(); - - std::uint32_t const uFlags(sleRippleState->getFieldU32(sfFlags)); - bool bDelete = false; - - // FIXME This NEEDS to be cleaned up and simplified. It's impossible - // for anyone to understand. - if (saBefore > beast::zero - // Sender balance was positive. - && saBalance <= beast::zero - // Sender is zero or negative. - && (uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) - // Sender reserve is set. - && static_cast(uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != - static_cast( - view.read(keylet::account(uSenderID))->getFlags() & lsfDefaultRipple) && - !(uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) && - !sleRippleState->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit) - // Sender trust limit is 0. - && !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) - // Sender quality in is 0. - && !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut)) - // Sender quality out is 0. - { - // Clear the reserve of the sender, possibly delete the line! - adjustOwnerCount(view, view.peek(keylet::account(uSenderID)), -1, j); - - // Clear reserve flag. - sleRippleState->setFieldU32( - sfFlags, uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); - - // Balance is zero, receiver reserve is clear. - bDelete = !saBalance // Balance is zero. - && !(uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)); - // Receiver reserve is clear. - } - - if (bSenderHigh) - saBalance.negate(); - - // Want to reflect balance to zero even if we are deleting line. - sleRippleState->setFieldAmount(sfBalance, saBalance); - // ONLY: Adjust ripple balance. - - if (bDelete) - { - return trustDelete( - view, - sleRippleState, - bSenderHigh ? uReceiverID : uSenderID, - !bSenderHigh ? uReceiverID : uSenderID, - j); - } - - view.update(sleRippleState); - return tesSUCCESS; - } - - STAmount const saReceiverLimit(Issue{currency, uReceiverID}); - STAmount saBalance{saAmount}; - - saBalance.setIssuer(noAccount()); - - JLOG(j.debug()) << "rippleCreditIOU: " - "create line: " - << to_string(uSenderID) << " -> " << to_string(uReceiverID) << " : " - << saAmount.getFullText(); - - auto const sleAccount = view.peek(keylet::account(uReceiverID)); - if (!sleAccount) - return tefINTERNAL; // LCOV_EXCL_LINE - - bool const noRipple = (sleAccount->getFlags() & lsfDefaultRipple) == 0; - - return trustCreate( - view, - bSenderHigh, - uSenderID, - uReceiverID, - index.key, - sleAccount, - false, - noRipple, - false, - false, - saBalance, - saReceiverLimit, - 0, - 0, - j); -} - -// Send regardless of limits. -// --> saAmount: Amount/currency/issuer to deliver to receiver. -// <-- saActual: Amount actually cost. Sender pays fees. -static TER -rippleSendIOU( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - STAmount& saActual, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - auto const& issuer = saAmount.getIssuer(); - - XRPL_ASSERT( - !isXRP(uSenderID) && !isXRP(uReceiverID), - "xrpl::rippleSendIOU : neither sender nor receiver is XRP"); - XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::rippleSendIOU : sender is not receiver"); - - if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount()) - { - // Direct send: redeeming IOUs and/or sending own IOUs. - auto const ter = rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, false, j); - if (ter != tesSUCCESS) - return ter; - saActual = saAmount; - return tesSUCCESS; - } - - // Sending 3rd party IOUs: transit. - - // Calculate the amount to transfer accounting - // for any transfer fees if the fee is not waived: - saActual = (waiveFee == WaiveTransferFee::Yes) ? saAmount - : multiply(saAmount, transferRate(view, issuer)); - - JLOG(j.debug()) << "rippleSendIOU> " << to_string(uSenderID) << " - > " - << to_string(uReceiverID) << " : deliver=" << saAmount.getFullText() - << " cost=" << saActual.getFullText(); - - TER terResult = rippleCreditIOU(view, issuer, uReceiverID, saAmount, true, j); - - if (tesSUCCESS == terResult) - terResult = rippleCreditIOU(view, uSenderID, issuer, saActual, true, j); - - return terResult; -} - -// Send regardless of limits. -// --> receivers: Amount/currency/issuer to deliver to receivers. -// <-- saActual: Amount actually cost to sender. Sender pays fees. -static TER -rippleSendMultiIOU( - ApplyView& view, - AccountID const& senderID, - Issue const& issue, - MultiplePaymentDestinations const& receivers, - STAmount& actual, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - auto const& issuer = issue.getIssuer(); - - XRPL_ASSERT(!isXRP(senderID), "xrpl::rippleSendMultiIOU : sender is not XRP"); - - // These may diverge - STAmount takeFromSender{issue}; - actual = takeFromSender; - - // Failures return immediately. - for (auto const& r : receivers) - { - auto const& receiverID = r.first; - STAmount amount{issue, r.second}; - - /* If we aren't sending anything or if the sender is the same as the - * receiver then we don't need to do anything. - */ - if (!amount || (senderID == receiverID)) - continue; - - XRPL_ASSERT(!isXRP(receiverID), "xrpl::rippleSendMultiIOU : receiver is not XRP"); - - if (senderID == issuer || receiverID == issuer || issuer == noAccount()) - { - // Direct send: redeeming IOUs and/or sending own IOUs. - if (auto const ter = rippleCreditIOU(view, senderID, receiverID, amount, false, j)) - return ter; - actual += amount; - // Do not add amount to takeFromSender, because rippleCreditIOU took - // it. - - continue; - } - - // Sending 3rd party IOUs: transit. - - // Calculate the amount to transfer accounting - // for any transfer fees if the fee is not waived: - STAmount actualSend = (waiveFee == WaiveTransferFee::Yes) - ? amount - : multiply(amount, transferRate(view, issuer)); - actual += actualSend; - takeFromSender += actualSend; - - JLOG(j.debug()) << "rippleSendMultiIOU> " << to_string(senderID) << " - > " - << to_string(receiverID) << " : deliver=" << amount.getFullText() - << " cost=" << actual.getFullText(); - - if (TER const terResult = rippleCreditIOU(view, issuer, receiverID, amount, true, j)) - return terResult; - } - - if (senderID != issuer && takeFromSender) - { - if (TER const terResult = rippleCreditIOU(view, senderID, issuer, takeFromSender, true, j)) - return terResult; - } - - return tesSUCCESS; -} - -static TER -accountSendIOU( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - if (view.rules().enabled(fixAMMv1_1)) - { - if (saAmount < beast::zero || saAmount.holds()) - { - return tecINTERNAL; // LCOV_EXCL_LINE - } - } - else - { - // LCOV_EXCL_START - XRPL_ASSERT( - saAmount >= beast::zero && !saAmount.holds(), - "xrpl::accountSendIOU : minimum amount and not MPT"); - // LCOV_EXCL_STOP - } - - /* If we aren't sending anything or if the sender is the same as the - * receiver then we don't need to do anything. - */ - if (!saAmount || (uSenderID == uReceiverID)) - return tesSUCCESS; - - if (!saAmount.native()) - { - STAmount saActual; - - JLOG(j.trace()) << "accountSendIOU: " << to_string(uSenderID) << " -> " - << to_string(uReceiverID) << " : " << saAmount.getFullText(); - - return rippleSendIOU(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee); - } - - /* XRP send which does not check reserve and can do pure adjustment. - * Note that sender or receiver may be null and this not a mistake; this - * setup is used during pathfinding and it is carefully controlled to - * ensure that transfers are balanced. - */ - TER terResult(tesSUCCESS); - - SLE::pointer sender = - uSenderID != beast::zero ? view.peek(keylet::account(uSenderID)) : SLE::pointer(); - SLE::pointer receiver = - uReceiverID != beast::zero ? view.peek(keylet::account(uReceiverID)) : SLE::pointer(); - - if (auto stream = j.trace()) - { - std::string sender_bal("-"); - std::string receiver_bal("-"); - - if (sender) - sender_bal = sender->getFieldAmount(sfBalance).getFullText(); - - if (receiver) - receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); - - stream << "accountSendIOU> " << to_string(uSenderID) << " (" << sender_bal << ") -> " - << to_string(uReceiverID) << " (" << receiver_bal - << ") : " << saAmount.getFullText(); - } - - if (sender) - { - if (sender->getFieldAmount(sfBalance) < saAmount) - { - // VFALCO Its laborious to have to mutate the - // TER based on params everywhere - // LCOV_EXCL_START - terResult = view.open() ? TER{telFAILED_PROCESSING} : TER{tecFAILED_PROCESSING}; - // LCOV_EXCL_STOP - } - else - { - auto const sndBal = sender->getFieldAmount(sfBalance); - view.creditHook(uSenderID, xrpAccount(), saAmount, sndBal); - - // Decrement XRP balance. - sender->setFieldAmount(sfBalance, sndBal - saAmount); - view.update(sender); - } - } - - if (tesSUCCESS == terResult && receiver) - { - // Increment XRP balance. - auto const rcvBal = receiver->getFieldAmount(sfBalance); - receiver->setFieldAmount(sfBalance, rcvBal + saAmount); - view.creditHook(xrpAccount(), uReceiverID, saAmount, -rcvBal); - - view.update(receiver); - } - - if (auto stream = j.trace()) - { - std::string sender_bal("-"); - std::string receiver_bal("-"); - - if (sender) - sender_bal = sender->getFieldAmount(sfBalance).getFullText(); - - if (receiver) - receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); - - stream << "accountSendIOU< " << to_string(uSenderID) << " (" << sender_bal << ") -> " - << to_string(uReceiverID) << " (" << receiver_bal - << ") : " << saAmount.getFullText(); - } - - return terResult; -} - -static TER -accountSendMultiIOU( - ApplyView& view, - AccountID const& senderID, - Issue const& issue, - MultiplePaymentDestinations const& receivers, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - XRPL_ASSERT_PARTS( - receivers.size() > 1, "xrpl::accountSendMultiIOU", "multiple recipients provided"); - - if (!issue.native()) - { - STAmount actual; - JLOG(j.trace()) << "accountSendMultiIOU: " << to_string(senderID) << " sending " - << receivers.size() << " IOUs"; - - return rippleSendMultiIOU(view, senderID, issue, receivers, actual, j, waiveFee); - } - - /* XRP send which does not check reserve and can do pure adjustment. - * Note that sender or receiver may be null and this not a mistake; this - * setup could be used during pathfinding and it is carefully controlled to - * ensure that transfers are balanced. - */ - - SLE::pointer sender = - senderID != beast::zero ? view.peek(keylet::account(senderID)) : SLE::pointer(); - - if (auto stream = j.trace()) - { - std::string sender_bal("-"); - - if (sender) - sender_bal = sender->getFieldAmount(sfBalance).getFullText(); - - stream << "accountSendMultiIOU> " << to_string(senderID) << " (" << sender_bal << ") -> " - << receivers.size() << " receivers."; - } - - // Failures return immediately. - STAmount takeFromSender{issue}; - for (auto const& r : receivers) - { - auto const& receiverID = r.first; - STAmount amount{issue, r.second}; - - if (amount < beast::zero) - { - return tecINTERNAL; // LCOV_EXCL_LINE - } - - /* If we aren't sending anything or if the sender is the same as the - * receiver then we don't need to do anything. - */ - if (!amount || (senderID == receiverID)) - continue; - - SLE::pointer receiver = - receiverID != beast::zero ? view.peek(keylet::account(receiverID)) : SLE::pointer(); - - if (auto stream = j.trace()) - { - std::string receiver_bal("-"); - - if (receiver) - receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); - - stream << "accountSendMultiIOU> " << to_string(senderID) << " -> " - << to_string(receiverID) << " (" << receiver_bal - << ") : " << amount.getFullText(); - } - - if (receiver) - { - // Increment XRP balance. - auto const rcvBal = receiver->getFieldAmount(sfBalance); - receiver->setFieldAmount(sfBalance, rcvBal + amount); - view.creditHook(xrpAccount(), receiverID, amount, -rcvBal); - - view.update(receiver); - - // Take what is actually sent - takeFromSender += amount; - } - - if (auto stream = j.trace()) - { - std::string receiver_bal("-"); - - if (receiver) - receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); - - stream << "accountSendMultiIOU< " << to_string(senderID) << " -> " - << to_string(receiverID) << " (" << receiver_bal - << ") : " << amount.getFullText(); - } - } - - if (sender) - { - if (sender->getFieldAmount(sfBalance) < takeFromSender) - { - return TER{tecFAILED_PROCESSING}; - } - else - { - auto const sndBal = sender->getFieldAmount(sfBalance); - view.creditHook(senderID, xrpAccount(), takeFromSender, sndBal); - - // Decrement XRP balance. - sender->setFieldAmount(sfBalance, sndBal - takeFromSender); - view.update(sender); - } - } - - if (auto stream = j.trace()) - { - std::string sender_bal("-"); - - if (sender) - sender_bal = sender->getFieldAmount(sfBalance).getFullText(); - - stream << "accountSendMultiIOU< " << to_string(senderID) << " (" << sender_bal << ") -> " - << receivers.size() << " receivers."; - } - return tesSUCCESS; -} - -static TER -rippleCreditMPT( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - beast::Journal j) -{ - // Do not check MPT authorization here - it must have been checked earlier - auto const mptID = keylet::mptIssuance(saAmount.get().getMptID()); - auto const& issuer = saAmount.getIssuer(); - auto sleIssuance = view.peek(mptID); - if (!sleIssuance) - return tecOBJECT_NOT_FOUND; - if (uSenderID == issuer) - { - (*sleIssuance)[sfOutstandingAmount] += saAmount.mpt().value(); - view.update(sleIssuance); - } - else - { - auto const mptokenID = keylet::mptoken(mptID.key, uSenderID); - if (auto sle = view.peek(mptokenID)) - { - auto const amt = sle->getFieldU64(sfMPTAmount); - auto const pay = saAmount.mpt().value(); - if (amt < pay) - return tecINSUFFICIENT_FUNDS; - (*sle)[sfMPTAmount] = amt - pay; - view.update(sle); - } - else - return tecNO_AUTH; - } - - if (uReceiverID == issuer) - { - auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount); - auto const redeem = saAmount.mpt().value(); - if (outstanding >= redeem) - { - sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem); - view.update(sleIssuance); - } - else - return tecINTERNAL; // LCOV_EXCL_LINE - } - else - { - auto const mptokenID = keylet::mptoken(mptID.key, uReceiverID); - if (auto sle = view.peek(mptokenID)) - { - (*sle)[sfMPTAmount] += saAmount.mpt().value(); - view.update(sle); - } - else - return tecNO_AUTH; - } - - return tesSUCCESS; -} - -static TER -rippleSendMPT( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - STAmount& saActual, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::rippleSendMPT : sender is not receiver"); - - // Safe to get MPT since rippleSendMPT is only called by accountSendMPT - auto const& issuer = saAmount.getIssuer(); - - auto const sle = view.read(keylet::mptIssuance(saAmount.get().getMptID())); - if (!sle) - return tecOBJECT_NOT_FOUND; - - if (uSenderID == issuer || uReceiverID == issuer) - { - // if sender is issuer, check that the new OutstandingAmount will not - // exceed MaximumAmount - if (uSenderID == issuer) - { - auto const sendAmount = saAmount.mpt().value(); - auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount); - if (sendAmount > maximumAmount || - sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount) - return tecPATH_DRY; - } - - // Direct send: redeeming MPTs and/or sending own MPTs. - auto const ter = rippleCreditMPT(view, uSenderID, uReceiverID, saAmount, j); - if (ter != tesSUCCESS) - return ter; - saActual = saAmount; - return tesSUCCESS; - } - - // Sending 3rd party MPTs: transit. - saActual = (waiveFee == WaiveTransferFee::Yes) - ? saAmount - : multiply(saAmount, transferRate(view, saAmount.get().getMptID())); - - JLOG(j.debug()) << "rippleSendMPT> " << to_string(uSenderID) << " - > " - << to_string(uReceiverID) << " : deliver=" << saAmount.getFullText() - << " cost=" << saActual.getFullText(); - - if (auto const terResult = rippleCreditMPT(view, issuer, uReceiverID, saAmount, j); - terResult != tesSUCCESS) - return terResult; - - return rippleCreditMPT(view, uSenderID, issuer, saActual, j); -} - -static TER -rippleSendMultiMPT( - ApplyView& view, - AccountID const& senderID, - MPTIssue const& mptIssue, - MultiplePaymentDestinations const& receivers, - STAmount& actual, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - // Safe to get MPT since rippleSendMultiMPT is only called by - // accountSendMultiMPT - auto const& issuer = mptIssue.getIssuer(); - - auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID())); - if (!sle) - return tecOBJECT_NOT_FOUND; - - // These may diverge - STAmount takeFromSender{mptIssue}; - actual = takeFromSender; - - for (auto const& r : receivers) - { - auto const& receiverID = r.first; - STAmount amount{mptIssue, r.second}; - - if (amount < beast::zero) - { - return tecINTERNAL; // LCOV_EXCL_LINE - } - - /* If we aren't sending anything or if the sender is the same as the - * receiver then we don't need to do anything. - */ - if (!amount || (senderID == receiverID)) - continue; - - if (senderID == issuer || receiverID == issuer) - { - // if sender is issuer, check that the new OutstandingAmount will - // not exceed MaximumAmount - if (senderID == issuer) - { - XRPL_ASSERT_PARTS( - takeFromSender == beast::zero, - "rippler::rippleSendMultiMPT", - "sender == issuer, takeFromSender == zero"); - auto const sendAmount = amount.mpt().value(); - auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount); - if (sendAmount > maximumAmount || - sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount) - return tecPATH_DRY; - } - - // Direct send: redeeming MPTs and/or sending own MPTs. - if (auto const ter = rippleCreditMPT(view, senderID, receiverID, amount, j)) - return ter; - actual += amount; - // Do not add amount to takeFromSender, because rippleCreditMPT took - // it - - continue; - } - - // Sending 3rd party MPTs: transit. - STAmount actualSend = (waiveFee == WaiveTransferFee::Yes) - ? amount - : multiply(amount, transferRate(view, amount.get().getMptID())); - actual += actualSend; - takeFromSender += actualSend; - - JLOG(j.debug()) << "rippleSendMultiMPT> " << to_string(senderID) << " - > " - << to_string(receiverID) << " : deliver=" << amount.getFullText() - << " cost=" << actualSend.getFullText(); - - if (auto const terResult = rippleCreditMPT(view, issuer, receiverID, amount, j)) - return terResult; - } - if (senderID != issuer && takeFromSender) - { - if (TER const terResult = rippleCreditMPT(view, senderID, issuer, takeFromSender, j)) - return terResult; - } - - return tesSUCCESS; -} - -static TER -accountSendMPT( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - XRPL_ASSERT( - saAmount >= beast::zero && saAmount.holds(), - "xrpl::accountSendMPT : minimum amount and MPT"); - - /* If we aren't sending anything or if the sender is the same as the - * receiver then we don't need to do anything. - */ - if (!saAmount || (uSenderID == uReceiverID)) - return tesSUCCESS; - - STAmount saActual{saAmount.asset()}; - - return rippleSendMPT(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee); -} - -static TER -accountSendMultiMPT( - ApplyView& view, - AccountID const& senderID, - MPTIssue const& mptIssue, - MultiplePaymentDestinations const& receivers, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - STAmount actual; - - return rippleSendMultiMPT(view, senderID, mptIssue, receivers, actual, j, waiveFee); -} - -TER -accountSend( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - return std::visit( - [&](TIss const& issue) { - if constexpr (std::is_same_v) - return accountSendIOU(view, uSenderID, uReceiverID, saAmount, j, waiveFee); - else - return accountSendMPT(view, uSenderID, uReceiverID, saAmount, j, waiveFee); - }, - saAmount.asset().value()); -} - -TER -accountSendMulti( - ApplyView& view, - AccountID const& senderID, - Asset const& asset, - MultiplePaymentDestinations const& receivers, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - XRPL_ASSERT_PARTS( - receivers.size() > 1, "xrpl::accountSendMulti", "multiple recipients provided"); - return std::visit( - [&](TIss const& issue) { - if constexpr (std::is_same_v) - return accountSendMultiIOU(view, senderID, issue, receivers, j, waiveFee); - else - return accountSendMultiMPT(view, senderID, issue, receivers, j, waiveFee); - }, - asset.value()); -} - -static bool -updateTrustLine( - ApplyView& view, - SLE::pointer state, - bool bSenderHigh, - AccountID const& sender, - STAmount const& before, - STAmount const& after, - beast::Journal j) -{ - if (!state) - return false; - std::uint32_t const flags(state->getFieldU32(sfFlags)); - - auto sle = view.peek(keylet::account(sender)); - if (!sle) - return false; - - // YYY Could skip this if rippling in reverse. - if (before > beast::zero - // Sender balance was positive. - && after <= beast::zero - // Sender is zero or negative. - && (flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) - // Sender reserve is set. - && static_cast(flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != - static_cast(sle->getFlags() & lsfDefaultRipple) && - !(flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) && - !state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit) - // Sender trust limit is 0. - && !state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) - // Sender quality in is 0. - && !state->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut)) - // Sender quality out is 0. - { - // VFALCO Where is the line being deleted? - // Clear the reserve of the sender, possibly delete the line! - adjustOwnerCount(view, sle, -1, j); - - // Clear reserve flag. - state->setFieldU32(sfFlags, flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); - - // Balance is zero, receiver reserve is clear. - if (!after // Balance is zero. - && !(flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve))) - return true; - } - return false; -} - -TER -issueIOU( - ApplyView& view, - AccountID const& account, - STAmount const& amount, - Issue const& issue, - beast::Journal j) -{ - XRPL_ASSERT( - !isXRP(account) && !isXRP(issue.account), - "xrpl::issueIOU : neither account nor issuer is XRP"); - - // Consistency check - XRPL_ASSERT(issue == amount.issue(), "xrpl::issueIOU : matching issue"); - - // Can't send to self! - XRPL_ASSERT(issue.account != account, "xrpl::issueIOU : not issuer account"); - - JLOG(j.trace()) << "issueIOU: " << to_string(account) << ": " << amount.getFullText(); - - bool bSenderHigh = issue.account > account; - - auto const index = keylet::line(issue.account, account, issue.currency); - - if (auto state = view.peek(index)) - { - STAmount final_balance = state->getFieldAmount(sfBalance); - - if (bSenderHigh) - final_balance.negate(); // Put balance in sender terms. - - STAmount const start_balance = final_balance; - - final_balance -= amount; - - auto const must_delete = updateTrustLine( - view, state, bSenderHigh, issue.account, start_balance, final_balance, j); - - view.creditHook(issue.account, account, amount, start_balance); - - if (bSenderHigh) - final_balance.negate(); - - // Adjust the balance on the trust line if necessary. We do this even if - // we are going to delete the line to reflect the correct balance at the - // time of deletion. - state->setFieldAmount(sfBalance, final_balance); - if (must_delete) - return trustDelete( - view, - state, - bSenderHigh ? account : issue.account, - bSenderHigh ? issue.account : account, - j); - - view.update(state); - - return tesSUCCESS; - } - - // NIKB TODO: The limit uses the receiver's account as the issuer and - // this is unnecessarily inefficient as copying which could be avoided - // is now required. Consider available options. - STAmount const limit(Issue{issue.currency, account}); - STAmount final_balance = amount; - - final_balance.setIssuer(noAccount()); - - auto const receiverAccount = view.peek(keylet::account(account)); - if (!receiverAccount) - return tefINTERNAL; // LCOV_EXCL_LINE - - bool noRipple = (receiverAccount->getFlags() & lsfDefaultRipple) == 0; - - return trustCreate( - view, - bSenderHigh, - issue.account, - account, - index.key, - receiverAccount, - false, - noRipple, - false, - false, - final_balance, - limit, - 0, - 0, - j); -} - -TER -redeemIOU( - ApplyView& view, - AccountID const& account, - STAmount const& amount, - Issue const& issue, - beast::Journal j) -{ - XRPL_ASSERT( - !isXRP(account) && !isXRP(issue.account), - "xrpl::redeemIOU : neither account nor issuer is XRP"); - - // Consistency check - XRPL_ASSERT(issue == amount.issue(), "xrpl::redeemIOU : matching issue"); - - // Can't send to self! - XRPL_ASSERT(issue.account != account, "xrpl::redeemIOU : not issuer account"); - - JLOG(j.trace()) << "redeemIOU: " << to_string(account) << ": " << amount.getFullText(); - - bool bSenderHigh = account > issue.account; - - if (auto state = view.peek(keylet::line(account, issue.account, issue.currency))) - { - STAmount final_balance = state->getFieldAmount(sfBalance); - - if (bSenderHigh) - final_balance.negate(); // Put balance in sender terms. - - STAmount const start_balance = final_balance; - - final_balance -= amount; - - auto const must_delete = - updateTrustLine(view, state, bSenderHigh, account, start_balance, final_balance, j); - - view.creditHook(account, issue.account, amount, start_balance); - - if (bSenderHigh) - final_balance.negate(); - - // Adjust the balance on the trust line if necessary. We do this even if - // we are going to delete the line to reflect the correct balance at the - // time of deletion. - state->setFieldAmount(sfBalance, final_balance); - - if (must_delete) - { - return trustDelete( - view, - state, - bSenderHigh ? issue.account : account, - bSenderHigh ? account : issue.account, - j); - } - - view.update(state); - return tesSUCCESS; - } - - // In order to hold an IOU, a trust line *MUST* exist to track the - // balance. If it doesn't, then something is very wrong. Don't try - // to continue. - // LCOV_EXCL_START - JLOG(j.fatal()) << "redeemIOU: " << to_string(account) << " attempts to redeem " - << amount.getFullText() << " but no trust line exists!"; - - return tefINTERNAL; - // LCOV_EXCL_STOP -} - -TER -transferXRP( - ApplyView& view, - AccountID const& from, - AccountID const& to, - STAmount const& amount, - beast::Journal j) -{ - XRPL_ASSERT(from != beast::zero, "xrpl::transferXRP : nonzero from account"); - XRPL_ASSERT(to != beast::zero, "xrpl::transferXRP : nonzero to account"); - XRPL_ASSERT(from != to, "xrpl::transferXRP : sender is not receiver"); - XRPL_ASSERT(amount.native(), "xrpl::transferXRP : amount is XRP"); - - SLE::pointer const sender = view.peek(keylet::account(from)); - SLE::pointer const receiver = view.peek(keylet::account(to)); - if (!sender || !receiver) - return tefINTERNAL; // LCOV_EXCL_LINE - - JLOG(j.trace()) << "transferXRP: " << to_string(from) << " -> " << to_string(to) - << ") : " << amount.getFullText(); - - if (sender->getFieldAmount(sfBalance) < amount) - { - // VFALCO Its unfortunate we have to keep - // mutating these TER everywhere - // FIXME: this logic should be moved to callers maybe? - // LCOV_EXCL_START - return view.open() ? TER{telFAILED_PROCESSING} : TER{tecFAILED_PROCESSING}; - // LCOV_EXCL_STOP - } - - // Decrement XRP balance. - sender->setFieldAmount(sfBalance, sender->getFieldAmount(sfBalance) - amount); - view.update(sender); - - receiver->setFieldAmount(sfBalance, receiver->getFieldAmount(sfBalance) + amount); - view.update(receiver); - - return tesSUCCESS; -} - -TER -requireAuth(ReadView const& view, Issue const& issue, AccountID const& account, AuthType authType) -{ - if (isXRP(issue) || issue.account == account) - return tesSUCCESS; - - auto const trustLine = view.read(keylet::line(account, issue.account, issue.currency)); - // If account has no line, and this is a strong check, fail - if (!trustLine && authType == AuthType::StrongAuth) - return tecNO_LINE; - - // If this is a weak or legacy check, or if the account has a line, fail if - // auth is required and not set on the line - if (auto const issuerAccount = view.read(keylet::account(issue.account)); - issuerAccount && (*issuerAccount)[sfFlags] & lsfRequireAuth) - { - if (trustLine) - return ((*trustLine)[sfFlags] & ((account > issue.account) ? lsfLowAuth : lsfHighAuth)) - ? tesSUCCESS - : TER{tecNO_AUTH}; - return TER{tecNO_LINE}; - } - - return tesSUCCESS; -} - -TER -requireAuth( - ReadView const& view, - MPTIssue const& mptIssue, - AccountID const& account, - AuthType authType, - int depth) -{ - auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); - auto const sleIssuance = view.read(mptID); - if (!sleIssuance) - return tecOBJECT_NOT_FOUND; - - auto const mptIssuer = sleIssuance->getAccountID(sfIssuer); - - // issuer is always "authorized" - if (mptIssuer == account) // Issuer won't have MPToken - return tesSUCCESS; - - bool const featureSAVEnabled = view.rules().enabled(featureSingleAssetVault); - - if (featureSAVEnabled) - { - if (depth >= maxAssetCheckDepth) - return tecINTERNAL; // LCOV_EXCL_LINE - - // requireAuth is recursive if the issuer is a vault pseudo-account - auto const sleIssuer = view.read(keylet::account(mptIssuer)); - if (!sleIssuer) - return tefINTERNAL; // LCOV_EXCL_LINE - - if (sleIssuer->isFieldPresent(sfVaultID)) - { - auto const sleVault = view.read(keylet::vault(sleIssuer->getFieldH256(sfVaultID))); - if (!sleVault) - return tefINTERNAL; // LCOV_EXCL_LINE - - auto const asset = sleVault->at(sfAsset); - if (auto const err = std::visit( - [&](TIss const& issue) { - if constexpr (std::is_same_v) - return requireAuth(view, issue, account, authType); - else - return requireAuth(view, issue, account, authType, depth + 1); - }, - asset.value()); - !isTesSuccess(err)) - return err; - } - } - - auto const mptokenID = keylet::mptoken(mptID.key, account); - auto const sleToken = view.read(mptokenID); - - // if account has no MPToken, fail - if (!sleToken && (authType == AuthType::StrongAuth || authType == AuthType::Legacy)) - return tecNO_AUTH; - - // Note, this check is not amendment-gated because DomainID will be always - // empty **unless** writing to it has been enabled by an amendment - auto const maybeDomainID = sleIssuance->at(~sfDomainID); - if (maybeDomainID) - { - XRPL_ASSERT( - sleIssuance->getFieldU32(sfFlags) & lsfMPTRequireAuth, - "xrpl::requireAuth : issuance requires authorization"); - // ter = tefINTERNAL | tecOBJECT_NOT_FOUND | tecNO_AUTH | tecEXPIRED - if (auto const ter = credentials::validDomain(view, *maybeDomainID, account); - isTesSuccess(ter)) - return ter; // Note: sleToken might be null - else if (!sleToken) - return ter; - // We ignore error from validDomain if we found sleToken, as it could - // belong to someone who is explicitly authorized e.g. a vault owner. - } - - if (featureSAVEnabled) - { - // Implicitly authorize Vault and LoanBroker pseudo-accounts - if (isPseudoAccount(view, account, {&sfVaultID, &sfLoanBrokerID})) - return tesSUCCESS; - } - - // mptoken must be authorized if issuance enabled requireAuth - if (sleIssuance->isFlag(lsfMPTRequireAuth) && - (!sleToken || !sleToken->isFlag(lsfMPTAuthorized))) - return tecNO_AUTH; - - return tesSUCCESS; // Note: sleToken might be null -} - -[[nodiscard]] TER -enforceMPTokenAuthorization( - ApplyView& view, - MPTID const& mptIssuanceID, - AccountID const& account, - XRPAmount const& priorBalance, // for MPToken authorization - beast::Journal j) -{ - auto const sleIssuance = view.read(keylet::mptIssuance(mptIssuanceID)); - if (!sleIssuance) - return tefINTERNAL; // LCOV_EXCL_LINE - - XRPL_ASSERT( - sleIssuance->isFlag(lsfMPTRequireAuth), - "xrpl::enforceMPTokenAuthorization : authorization required"); - - if (account == sleIssuance->at(sfIssuer)) - return tefINTERNAL; // LCOV_EXCL_LINE - - auto const keylet = keylet::mptoken(mptIssuanceID, account); - auto const sleToken = view.read(keylet); // NOTE: might be null - auto const maybeDomainID = sleIssuance->at(~sfDomainID); - bool expired = false; - bool const authorizedByDomain = [&]() -> bool { - // NOTE: defensive here, should be checked in preclaim - if (!maybeDomainID.has_value()) - return false; // LCOV_EXCL_LINE - - auto const ter = verifyValidDomain(view, account, *maybeDomainID, j); - if (isTesSuccess(ter)) - return true; - if (ter == tecEXPIRED) - expired = true; - return false; - }(); - - if (!authorizedByDomain && sleToken == nullptr) - { - // Could not find MPToken and won't create one, could be either of: - // - // 1. Field sfDomainID not set in MPTokenIssuance or - // 2. Account has no matching and accepted credentials or - // 3. Account has all expired credentials (deleted in verifyValidDomain) - // - // Either way, return tecNO_AUTH and there is nothing else to do - return expired ? tecEXPIRED : tecNO_AUTH; - } - else if (!authorizedByDomain && maybeDomainID.has_value()) - { - // Found an MPToken but the account is not authorized and we expect - // it to have been authorized by the domain. This could be because the - // credentials used to create the MPToken have expired or been deleted. - return expired ? tecEXPIRED : tecNO_AUTH; - } - else if (!authorizedByDomain) - { - // We found an MPToken, but sfDomainID is not set, so this is a classic - // MPToken which requires authorization by the token issuer. - XRPL_ASSERT( - sleToken != nullptr && !maybeDomainID.has_value(), - "xrpl::enforceMPTokenAuthorization : found MPToken"); - if (sleToken->isFlag(lsfMPTAuthorized)) - return tesSUCCESS; - - return tecNO_AUTH; - } - else if (authorizedByDomain && sleToken != nullptr) - { - // Found an MPToken, authorized by the domain. Ignore authorization flag - // lsfMPTAuthorized because it is meaningless. Return tesSUCCESS - XRPL_ASSERT( - maybeDomainID.has_value(), - "xrpl::enforceMPTokenAuthorization : found MPToken for domain"); - return tesSUCCESS; - } - else if (authorizedByDomain) - { - // Could not find MPToken but there should be one because we are - // authorized by domain. Proceed to create it, then return tesSUCCESS - XRPL_ASSERT( - maybeDomainID.has_value() && sleToken == nullptr, - "xrpl::enforceMPTokenAuthorization : new MPToken for domain"); - if (auto const err = authorizeMPToken( - view, - priorBalance, // priorBalance - mptIssuanceID, // mptIssuanceID - account, // account - j); - !isTesSuccess(err)) - return err; - - return tesSUCCESS; - } - - // LCOV_EXCL_START - UNREACHABLE("xrpl::enforceMPTokenAuthorization : condition list is incomplete"); - return tefINTERNAL; - // LCOV_EXCL_STOP -} - -TER -canTransfer( - ReadView const& view, - MPTIssue const& mptIssue, - AccountID const& from, - AccountID const& to) -{ - auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); - auto const sleIssuance = view.read(mptID); - if (!sleIssuance) - return tecOBJECT_NOT_FOUND; - - if (!(sleIssuance->getFieldU32(sfFlags) & lsfMPTCanTransfer)) - { - if (from != (*sleIssuance)[sfIssuer] && to != (*sleIssuance)[sfIssuer]) - return TER{tecNO_AUTH}; - } - return tesSUCCESS; -} - -[[nodiscard]] TER -canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, AccountID const& to) -{ - if (issue.native()) - return tesSUCCESS; - - auto const& issuerId = issue.getIssuer(); - if (issuerId == from || issuerId == to) - return tesSUCCESS; - auto const sleIssuer = view.read(keylet::account(issuerId)); - if (sleIssuer == nullptr) - return tefINTERNAL; // LCOV_EXCL_LINE - - auto const isRippleDisabled = [&](AccountID account) -> bool { - // Line might not exist, but some transfers can create it. If this - // is the case, just check the default ripple on the issuer account. - auto const line = view.read(keylet::line(account, issue)); - if (line) - { - bool const issuerHigh = issuerId > account; - return line->isFlag(issuerHigh ? lsfHighNoRipple : lsfLowNoRipple); - } - return sleIssuer->isFlag(lsfDefaultRipple) == false; - }; - - // Fail if rippling disabled on both trust lines - if (isRippleDisabled(from) && isRippleDisabled(to)) - return terNO_RIPPLE; - - return tesSUCCESS; -} - TER cleanupOnAccountDelete( ApplyView& view, @@ -3190,7 +483,7 @@ cleanupOnAccountDelete( // Deleter handles the details of specific account-owned object // deletion auto const [ter, skipEntry] = deleter(nodeType, dirEntry, sleItem); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return ter; // dirFirst() and dirNext() are like iterators with exposed @@ -3226,427 +519,6 @@ cleanupOnAccountDelete( return tesSUCCESS; } -TER -deleteAMMTrustLine( - ApplyView& view, - std::shared_ptr sleState, - std::optional const& ammAccountID, - beast::Journal j) -{ - if (!sleState || sleState->getType() != ltRIPPLE_STATE) - return tecINTERNAL; // LCOV_EXCL_LINE - - auto const& [low, high] = std::minmax( - sleState->getFieldAmount(sfLowLimit).getIssuer(), - sleState->getFieldAmount(sfHighLimit).getIssuer()); - auto sleLow = view.peek(keylet::account(low)); - auto sleHigh = view.peek(keylet::account(high)); - if (!sleLow || !sleHigh) - return tecINTERNAL; // LCOV_EXCL_LINE - - bool const ammLow = sleLow->isFieldPresent(sfAMMID); - bool const ammHigh = sleHigh->isFieldPresent(sfAMMID); - - // can't both be AMM - if (ammLow && ammHigh) - return tecINTERNAL; // LCOV_EXCL_LINE - - // at least one must be - if (!ammLow && !ammHigh) - return terNO_AMM; - - // one must be the target amm - if (ammAccountID && (low != *ammAccountID && high != *ammAccountID)) - return terNO_AMM; - - if (auto const ter = trustDelete(view, sleState, low, high, j); ter != tesSUCCESS) - { - JLOG(j.error()) << "deleteAMMTrustLine: failed to delete the trustline."; - return ter; - } - - auto const uFlags = !ammLow ? lsfLowReserve : lsfHighReserve; - if (!(sleState->getFlags() & uFlags)) - return tecINTERNAL; // LCOV_EXCL_LINE - - adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j); - - return tesSUCCESS; -} - -TER -rippleCredit( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - bool bCheckIssuer, - beast::Journal j) -{ - return std::visit( - [&](TIss const& issue) { - if constexpr (std::is_same_v) - { - return rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j); - } - else - { - XRPL_ASSERT(!bCheckIssuer, "xrpl::rippleCredit : not checking issuer"); - return rippleCreditMPT(view, uSenderID, uReceiverID, saAmount, j); - } - }, - saAmount.asset().value()); -} - -[[nodiscard]] std::optional -assetsToSharesDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& assets) -{ - XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesDeposit : non-negative assets"); - XRPL_ASSERT( - assets.asset() == vault->at(sfAsset), - "xrpl::assetsToSharesDeposit : assets and vault match"); - if (assets.negative() || assets.asset() != vault->at(sfAsset)) - return std::nullopt; // LCOV_EXCL_LINE - - Number const assetTotal = vault->at(sfAssetsTotal); - STAmount shares{vault->at(sfShareMPTID)}; - if (assetTotal == 0) - return STAmount{ - shares.asset(), - Number(assets.mantissa(), assets.exponent() + vault->at(sfScale)).truncate()}; - - Number const shareTotal = issuance->at(sfOutstandingAmount); - shares = ((shareTotal * assets) / assetTotal).truncate(); - return shares; -} - -[[nodiscard]] std::optional -sharesToAssetsDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& shares) -{ - XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsDeposit : non-negative shares"); - XRPL_ASSERT( - shares.asset() == vault->at(sfShareMPTID), - "xrpl::sharesToAssetsDeposit : shares and vault match"); - if (shares.negative() || shares.asset() != vault->at(sfShareMPTID)) - return std::nullopt; // LCOV_EXCL_LINE - - Number const assetTotal = vault->at(sfAssetsTotal); - STAmount assets{vault->at(sfAsset)}; - if (assetTotal == 0) - return STAmount{ - assets.asset(), shares.mantissa(), shares.exponent() - vault->at(sfScale), false}; - - Number const shareTotal = issuance->at(sfOutstandingAmount); - assets = (assetTotal * shares) / shareTotal; - return assets; -} - -[[nodiscard]] std::optional -assetsToSharesWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& assets, - TruncateShares truncate) -{ - XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesDeposit : non-negative assets"); - XRPL_ASSERT( - assets.asset() == vault->at(sfAsset), - "xrpl::assetsToSharesWithdraw : assets and vault match"); - if (assets.negative() || assets.asset() != vault->at(sfAsset)) - return std::nullopt; // LCOV_EXCL_LINE - - Number assetTotal = vault->at(sfAssetsTotal); - assetTotal -= vault->at(sfLossUnrealized); - STAmount shares{vault->at(sfShareMPTID)}; - if (assetTotal == 0) - return shares; - Number const shareTotal = issuance->at(sfOutstandingAmount); - Number result = (shareTotal * assets) / assetTotal; - if (truncate == TruncateShares::yes) - result = result.truncate(); - shares = result; - return shares; -} - -[[nodiscard]] std::optional -sharesToAssetsWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& shares) -{ - XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsDeposit : non-negative shares"); - XRPL_ASSERT( - shares.asset() == vault->at(sfShareMPTID), - "xrpl::sharesToAssetsWithdraw : shares and vault match"); - if (shares.negative() || shares.asset() != vault->at(sfShareMPTID)) - return std::nullopt; // LCOV_EXCL_LINE - - Number assetTotal = vault->at(sfAssetsTotal); - assetTotal -= vault->at(sfLossUnrealized); - STAmount assets{vault->at(sfAsset)}; - if (assetTotal == 0) - return assets; - Number const shareTotal = issuance->at(sfOutstandingAmount); - assets = (assetTotal * shares) / shareTotal; - return assets; -} - -TER -rippleLockEscrowMPT( - ApplyView& view, - AccountID const& sender, - STAmount const& amount, - beast::Journal j) -{ - auto const mptIssue = amount.get(); - auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); - auto sleIssuance = view.peek(mptID); - if (!sleIssuance) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleLockEscrowMPT: MPT issuance not found for " - << mptIssue.getMptID(); - return tecOBJECT_NOT_FOUND; - } // LCOV_EXCL_STOP - - if (amount.getIssuer() == sender) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleLockEscrowMPT: sender is the issuer, cannot lock MPTs."; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - // 1. Decrease the MPT Holder MPTAmount - // 2. Increase the MPT Holder EscrowedAmount - { - auto const mptokenID = keylet::mptoken(mptID.key, sender); - auto sle = view.peek(mptokenID); - if (!sle) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleLockEscrowMPT: MPToken not found for " << sender; - return tecOBJECT_NOT_FOUND; - } // LCOV_EXCL_STOP - - auto const amt = sle->getFieldU64(sfMPTAmount); - auto const pay = amount.mpt().value(); - - // Underflow check for subtraction - if (!canSubtract(STAmount(mptIssue, amt), STAmount(mptIssue, pay))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleLockEscrowMPT: insufficient MPTAmount for " - << to_string(sender) << ": " << amt << " < " << pay; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - (*sle)[sfMPTAmount] = amt - pay; - - // Overflow check for addition - uint64_t const locked = (*sle)[~sfLockedAmount].value_or(0); - - if (!canAdd(STAmount(mptIssue, locked), STAmount(mptIssue, pay))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleLockEscrowMPT: overflow on locked amount for " - << to_string(sender) << ": " << locked << " + " << pay; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - if (sle->isFieldPresent(sfLockedAmount)) - (*sle)[sfLockedAmount] += pay; - else - sle->setFieldU64(sfLockedAmount, pay); - - view.update(sle); - } - - // 1. Increase the Issuance EscrowedAmount - // 2. DO NOT change the Issuance OutstandingAmount - { - uint64_t const issuanceEscrowed = (*sleIssuance)[~sfLockedAmount].value_or(0); - auto const pay = amount.mpt().value(); - - // Overflow check for addition - if (!canAdd(STAmount(mptIssue, issuanceEscrowed), STAmount(mptIssue, pay))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleLockEscrowMPT: overflow on issuance " - "locked amount for " - << mptIssue.getMptID() << ": " << issuanceEscrowed << " + " << pay; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - if (sleIssuance->isFieldPresent(sfLockedAmount)) - (*sleIssuance)[sfLockedAmount] += pay; - else - sleIssuance->setFieldU64(sfLockedAmount, pay); - - view.update(sleIssuance); - } - return tesSUCCESS; -} - -TER -rippleUnlockEscrowMPT( - ApplyView& view, - AccountID const& sender, - AccountID const& receiver, - STAmount const& netAmount, - STAmount const& grossAmount, - beast::Journal j) -{ - if (!view.rules().enabled(fixTokenEscrowV1)) - XRPL_ASSERT( - netAmount == grossAmount, "xrpl::rippleUnlockEscrowMPT : netAmount == grossAmount"); - - auto const& issuer = netAmount.getIssuer(); - auto const& mptIssue = netAmount.get(); - auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); - auto sleIssuance = view.peek(mptID); - if (!sleIssuance) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: MPT issuance not found for " - << mptIssue.getMptID(); - return tecOBJECT_NOT_FOUND; - } // LCOV_EXCL_STOP - - // Decrease the Issuance EscrowedAmount - { - if (!sleIssuance->isFieldPresent(sfLockedAmount)) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: no locked amount in issuance for " - << mptIssue.getMptID(); - return tecINTERNAL; - } // LCOV_EXCL_STOP - - auto const locked = sleIssuance->getFieldU64(sfLockedAmount); - auto const redeem = grossAmount.mpt().value(); - - // Underflow check for subtraction - if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, redeem))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: insufficient locked amount for " - << mptIssue.getMptID() << ": " << locked << " < " << redeem; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - auto const newLocked = locked - redeem; - if (newLocked == 0) - sleIssuance->makeFieldAbsent(sfLockedAmount); - else - sleIssuance->setFieldU64(sfLockedAmount, newLocked); - view.update(sleIssuance); - } - - if (issuer != receiver) - { - // Increase the MPT Holder MPTAmount - auto const mptokenID = keylet::mptoken(mptID.key, receiver); - auto sle = view.peek(mptokenID); - if (!sle) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: MPToken not found for " << receiver; - return tecOBJECT_NOT_FOUND; - } // LCOV_EXCL_STOP - - auto current = sle->getFieldU64(sfMPTAmount); - auto delta = netAmount.mpt().value(); - - // Overflow check for addition - if (!canAdd(STAmount(mptIssue, current), STAmount(mptIssue, delta))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: overflow on MPTAmount for " - << to_string(receiver) << ": " << current << " + " << delta; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - (*sle)[sfMPTAmount] += delta; - view.update(sle); - } - else - { - // Decrease the Issuance OutstandingAmount - auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount); - auto const redeem = netAmount.mpt().value(); - - // Underflow check for subtraction - if (!canSubtract(STAmount(mptIssue, outstanding), STAmount(mptIssue, redeem))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: insufficient outstanding amount for " - << mptIssue.getMptID() << ": " << outstanding << " < " << redeem; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem); - view.update(sleIssuance); - } - - if (issuer == sender) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: sender is the issuer, " - "cannot unlock MPTs."; - return tecINTERNAL; - } // LCOV_EXCL_STOP - else - { - // Decrease the MPT Holder EscrowedAmount - auto const mptokenID = keylet::mptoken(mptID.key, sender); - auto sle = view.peek(mptokenID); - if (!sle) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: MPToken not found for " << sender; - return tecOBJECT_NOT_FOUND; - } // LCOV_EXCL_STOP - - if (!sle->isFieldPresent(sfLockedAmount)) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: no locked amount in MPToken for " - << to_string(sender); - return tecINTERNAL; - } // LCOV_EXCL_STOP - - auto const locked = sle->getFieldU64(sfLockedAmount); - auto const delta = grossAmount.mpt().value(); - - // Underflow check for subtraction - if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, delta))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: insufficient locked amount for " - << to_string(sender) << ": " << locked << " < " << delta; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - auto const newLocked = locked - delta; - if (newLocked == 0) - sle->makeFieldAbsent(sfLockedAmount); - else - sle->setFieldU64(sfLockedAmount, newLocked); - view.update(sle); - } - - // Note: The gross amount is the amount that was locked, the net - // amount is the amount that is being unlocked. The difference is the fee - // that was charged for the transfer. If this difference is greater than - // zero, we need to update the outstanding amount. - auto const diff = grossAmount.mpt().value() - netAmount.mpt().value(); - if (diff != 0) - { - auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount); - // Underflow check for subtraction - if (!canSubtract(STAmount(mptIssue, outstanding), STAmount(mptIssue, diff))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: insufficient outstanding amount for " - << mptIssue.getMptID() << ": " << outstanding << " < " << diff; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - diff); - view.update(sleIssuance); - } - return tesSUCCESS; -} - bool after(NetClock::time_point now, std::uint32_t mark) { diff --git a/src/libxrpl/tx/transactors/dex/AMMHelpers.cpp b/src/libxrpl/ledger/helpers/AMMHelpers.cpp similarity index 87% rename from src/libxrpl/tx/transactors/dex/AMMHelpers.cpp rename to src/libxrpl/ledger/helpers/AMMHelpers.cpp index c232f82543..c65bb1ff11 100644 --- a/src/libxrpl/tx/transactors/dex/AMMHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AMMHelpers.cpp @@ -1,4 +1,4 @@ -#include +#include namespace xrpl { @@ -7,7 +7,7 @@ ammLPTokens(STAmount const& asset1, STAmount const& asset2, Issue const& lptIssu { // AMM invariant: sqrt(asset1 * asset2) >= LPTokensBalance auto const rounding = isFeatureEnabled(fixAMMv1_3) ? Number::downward : Number::getround(); - NumberRoundModeGuard g(rounding); + NumberRoundModeGuard const g(rounding); auto const tokens = root2(asset1 * asset2); return toSTAmount(lptIssue, tokens); } @@ -34,12 +34,10 @@ lpTokensOut( auto const t = lptAMMBalance * (r - c) / (1 + c); return toSTAmount(lptAMMBalance.issue(), t); } - else - { - // minimize tokens out - auto const frac = (r - c) / (1 + c); - return multiply(lptAMMBalance, frac, Number::downward); - } + + // minimize tokens out + auto const frac = (r - c) / (1 + c); + return multiply(lptAMMBalance, frac, Number::downward); } /* Equation 4 solves equation 3 for b: @@ -72,12 +70,10 @@ ammAssetIn( { return toSTAmount(asset1Balance.issue(), asset1Balance * solveQuadraticEq(a, b, c)); } - else - { - // maximize deposit - auto const frac = solveQuadraticEq(a, b, c); - return multiply(asset1Balance, frac, Number::upward); - } + + // maximize deposit + auto const frac = solveQuadraticEq(a, b, c); + return multiply(asset1Balance, frac, Number::upward); } /* Equation 7: @@ -99,12 +95,10 @@ lpTokensIn( auto const t = lptAMMBalance * (c - root2(c * c - 4 * fr)) / 2; return toSTAmount(lptAMMBalance.issue(), t); } - else - { - // maximize tokens in - auto const frac = (c - root2(c * c - 4 * fr)) / 2; - return multiply(lptAMMBalance, frac, Number::upward); - } + + // maximize tokens in + auto const frac = (c - root2(c * c - 4 * fr)) / 2; + return multiply(lptAMMBalance, frac, Number::upward); } /* Equation 8 solves equation 7 for b: @@ -131,12 +125,10 @@ ammAssetOut( auto const b = assetBalance * (t1 * t1 - t1 * (2 - f)) / (t1 * f - 1); return toSTAmount(assetBalance.issue(), b); } - else - { - // minimize withdraw - auto const frac = (t1 * t1 - t1 * (2 - f)) / (t1 * f - 1); - return multiply(assetBalance, frac, Number::downward); - } + + // minimize withdraw + auto const frac = (t1 * t1 - t1 * (2 - f)) / (t1 * f - 1); + return multiply(assetBalance, frac, Number::downward); } Number @@ -150,7 +142,7 @@ adjustLPTokens(STAmount const& lptAMMBalance, STAmount const& lpTokens, IsDeposi { // Force rounding downward to ensure adjusted tokens are less or equal // to requested tokens. - saveNumberRoundMode rm(Number::setround(Number::rounding_mode::downward)); + saveNumberRoundMode const rm(Number::setround(Number::rounding_mode::downward)); if (isDeposit == IsDeposit::Yes) return (lptAMMBalance + lpTokens) - lptAMMBalance; return (lpTokens - lptAMMBalance) + lptAMMBalance; @@ -194,29 +186,37 @@ adjustAmountsByLPTokens( auto const amountActual = toSTAmount(amount.issue(), fr * amount); auto const amount2Actual = toSTAmount(amount2->issue(), fr * *amount2); if (!ammRoundingEnabled) + { return std::make_tuple( amountActual < amount ? amountActual : amount, amount2Actual < amount2 ? amount2Actual : amount2, lpTokensActual); - else - return std::make_tuple(amountActual, amount2Actual, lpTokensActual); + } + + return std::make_tuple(amountActual, amount2Actual, lpTokensActual); } // Single trade auto const amountActual = [&]() { if (isDeposit == IsDeposit::Yes) + { return ammAssetIn(amountBalance, lptAMMBalance, lpTokensActual, tfee); - else if (!ammRoundingEnabled) + } + if (!ammRoundingEnabled) + { return ammAssetOut(amountBalance, lptAMMBalance, lpTokens, tfee); - else - return ammAssetOut(amountBalance, lptAMMBalance, lpTokensActual, tfee); + } + + return ammAssetOut(amountBalance, lptAMMBalance, lpTokensActual, tfee); }(); if (!ammRoundingEnabled) + { return amountActual < amount ? std::make_tuple(amountActual, std::nullopt, lpTokensActual) : std::make_tuple(amount, std::nullopt, lpTokensActual); - else - return std::make_tuple(amountActual, std::nullopt, lpTokensActual); + } + + return std::make_tuple(amountActual, std::nullopt, lpTokensActual); } XRPL_ASSERT( @@ -241,15 +241,17 @@ solveQuadraticEqSmallest(Number const& a, Number const& b, Number const& c) // use numerically stable citardauq formula for quadratic equation solution // https://people.csail.mit.edu/bkph/articles/Quadratics.pdf if (b > 0) + { return (2 * c) / (-b - root2(d)); - else - return (2 * c) / (-b + root2(d)); + } + + return (2 * c) / (-b + root2(d)); } STAmount multiply(STAmount const& amount, Number const& frac, Number::rounding_mode rm) { - NumberRoundModeGuard g(rm); + NumberRoundModeGuard const g(rm); auto const t = amount * frac; return toSTAmount(amount.issue(), t, rm); } @@ -257,9 +259,9 @@ multiply(STAmount const& amount, Number const& frac, Number::rounding_mode rm) STAmount getRoundedAsset( Rules const& rules, - std::function&& noRoundCb, + std::function const& noRoundCb, STAmount const& balance, - std::function&& productCb, + std::function const& productCb, IsDeposit isDeposit) { if (!rules.enabled(fixAMMv1_3)) @@ -268,7 +270,7 @@ getRoundedAsset( auto const rm = detail::getAssetRounding(isDeposit); if (isDeposit == IsDeposit::Yes) return multiply(balance, productCb(), rm); - NumberRoundModeGuard g(rm); + NumberRoundModeGuard const g(rm); return toSTAmount(balance.issue(), productCb(), rm); } @@ -290,9 +292,9 @@ getRoundedLPTokens( STAmount getRoundedLPTokens( Rules const& rules, - std::function&& noRoundCb, + std::function const& noRoundCb, STAmount const& lptAMMBalance, - std::function&& productCb, + std::function const& productCb, IsDeposit isDeposit) { if (!rules.enabled(fixAMMv1_3)) @@ -302,7 +304,7 @@ getRoundedLPTokens( auto const rm = detail::getLPTokenRounding(isDeposit); if (isDeposit == IsDeposit::Yes) { - NumberRoundModeGuard g(rm); + NumberRoundModeGuard const g(rm); return toSTAmount(lptAMMBalance.issue(), productCb(), rm); } return multiply(lptAMMBalance, productCb(), rm); diff --git a/src/libxrpl/tx/transactors/dex/AMMUtils.cpp b/src/libxrpl/ledger/helpers/AMMUtils.cpp similarity index 95% rename from src/libxrpl/tx/transactors/dex/AMMUtils.cpp rename to src/libxrpl/ledger/helpers/AMMUtils.cpp index ce44c9ffd6..4b71a2e438 100644 --- a/src/libxrpl/tx/transactors/dex/AMMUtils.cpp +++ b/src/libxrpl/ledger/helpers/AMMUtils.cpp @@ -1,9 +1,10 @@ #include +#include #include +#include +#include #include #include -#include -#include namespace xrpl { @@ -51,9 +52,13 @@ ammHolds( Issue checkIssue, char const* label) -> std::optional> { if (checkIssue == issue1) + { return std::make_optional(std::make_pair(issue1, issue2)); - else if (checkIssue == issue2) + } + if (checkIssue == issue2) + { return std::make_optional(std::make_pair(issue2, issue1)); + } // Unreachable unless AMM corrupted. // LCOV_EXCL_START JLOG(j.debug()) << "ammHolds: Invalid " << label << " " << checkIssue; @@ -64,7 +69,7 @@ ammHolds( { return singleIssue(*optIssue1, "optIssue1"); } - else if (optIssue2) + if (optIssue2) { // Cannot have Amount2 without Amount. return singleIssue(*optIssue2, "optIssue2"); // LCOV_EXCL_LINE @@ -153,7 +158,7 @@ getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account) "xrpl::getTradingFee : auction present"); if (ammSle.isFieldPresent(sfAuctionSlot)) { - auto const& auctionSlot = static_cast(ammSle.peekAtField(sfAuctionSlot)); + auto const& auctionSlot = safe_downcast(ammSle.peekAtField(sfAuctionSlot)); // Not expired if (auto const expiration = auctionSlot[~sfExpiration]; duration_cast(view.header().parentCloseTime.time_since_epoch()).count() < @@ -164,8 +169,10 @@ getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account) if (auctionSlot.isFieldPresent(sfAuthAccounts)) { for (auto const& acct : auctionSlot.getFieldArray(sfAuthAccounts)) + { if (acct[~sfAccount] == account) return auctionSlot[sfDiscountedFee]; + } } } } @@ -259,7 +266,7 @@ deleteAMMAccount(Sandbox& sb, Issue const& asset, Issue const& asset2, beast::Jo } if (auto const ter = deleteAMMTrustLines(sb, ammAccountID, maxDeletableAMMTrustLines, j); - ter != tesSUCCESS) + !isTesSuccess(ter)) return ter; auto const ownerDirKeylet = keylet::ownerDir(ammAccountID); @@ -322,13 +329,21 @@ initializeFeeAuctionVote( auctionSlot.setFieldAmount(sfPrice, STAmount{lptIssue, 0}); // Set the fee if (tfee != 0) + { ammSle->setFieldU16(sfTradingFee, tfee); + } else if (ammSle->isFieldPresent(sfTradingFee)) + { ammSle->makeFieldAbsent(sfTradingFee); // LCOV_EXCL_LINE + } if (auto const dfee = tfee / AUCTION_SLOT_DISCOUNTED_FEE_FRACTION) + { auctionSlot.setFieldU16(sfDiscountedFee, dfee); + } else if (auctionSlot.isFieldPresent(sfDiscountedFee)) + { auctionSlot.makeFieldAbsent(sfDiscountedFee); // LCOV_EXCL_LINE + } } Expected @@ -389,13 +404,19 @@ isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID c return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE } else if (++nIOUTrustLines > 2) + { return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + } } // Another Liquidity Provider LPToken trustline else if (isLPTokenTrustline) + { return false; + } else if (++nIOUTrustLines > 2) + { return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + } } auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext); if (uNodeNext == 0) @@ -416,9 +437,13 @@ verifyAndAdjustLPTokenBalance( std::shared_ptr& ammSle, AccountID const& account) { - if (auto const res = isOnlyLiquidityProvider(sb, lpTokens.issue(), account); !res) + auto const res = isOnlyLiquidityProvider(sb, lpTokens.issue(), account); + if (!res.has_value()) + { return Unexpected(res.error()); - else if (res.value()) + } + + if (res.value()) { if (withinRelativeDistance( lpTokens, ammSle->getFieldAmount(sfLPTokenBalance), Number{1, -3})) diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp new file mode 100644 index 0000000000..399494bc5f --- /dev/null +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -0,0 +1,247 @@ +#include +// +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +bool +isGlobalFrozen(ReadView const& view, AccountID const& issuer) +{ + if (isXRP(issuer)) + return false; + if (auto const sle = view.read(keylet::account(issuer))) + return sle->isFlag(lsfGlobalFreeze); + return false; +} + +// An owner count cannot be negative. If adjustment would cause a negative +// owner count, clamp the owner count at 0. Similarly for overflow. This +// adjustment allows the ownerCount to be adjusted up or down in multiple steps. +// If id != std::nullopt, then do error reporting. +// +// Returns adjusted owner count. +static std::uint32_t +confineOwnerCount( + std::uint32_t current, + std::int32_t adjustment, + std::optional const& id = std::nullopt, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) +{ + std::uint32_t adjusted{current + adjustment}; + if (adjustment > 0) + { + // Overflow is well defined on unsigned + if (adjusted < current) + { + if (id) + { + JLOG(j.fatal()) << "Account " << *id << " owner count exceeds max!"; + } + adjusted = std::numeric_limits::max(); + } + } + else + { + // Underflow is well defined on unsigned + if (adjusted > current) + { + if (id) + { + JLOG(j.fatal()) << "Account " << *id << " owner count set below 0!"; + } + adjusted = 0; + XRPL_ASSERT(!id, "xrpl::confineOwnerCount : id is not set"); + } + } + return adjusted; +} + +XRPAmount +xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j) +{ + auto const sle = view.read(keylet::account(id)); + if (sle == nullptr) + return beast::zero; + + // Return balance minus reserve + std::uint32_t const ownerCount = + confineOwnerCount(view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj); + + // Pseudo-accounts have no reserve requirement + auto const reserve = + isPseudoAccount(sle) ? XRPAmount{0} : view.fees().accountReserve(ownerCount); + + auto const fullBalance = sle->getFieldAmount(sfBalance); + + auto const balance = view.balanceHook(id, xrpAccount(), fullBalance); + + STAmount const amount = (balance < reserve) ? STAmount{0} : balance - reserve; + + JLOG(j.trace()) << "accountHolds:" << " account=" << to_string(id) + << " amount=" << amount.getFullText() + << " fullBalance=" << fullBalance.getFullText() + << " balance=" << balance.getFullText() << " reserve=" << reserve + << " ownerCount=" << ownerCount << " ownerCountAdj=" << ownerCountAdj; + + return amount.xrp(); +} + +Rate +transferRate(ReadView const& view, AccountID const& issuer) +{ + auto const sle = view.read(keylet::account(issuer)); + + if (sle && sle->isFieldPresent(sfTransferRate)) + return Rate{sle->getFieldU32(sfTransferRate)}; + + return parityRate; +} + +void +adjustOwnerCount( + ApplyView& view, + std::shared_ptr const& sle, + std::int32_t amount, + beast::Journal j) +{ + if (!sle) + return; + XRPL_ASSERT(amount, "xrpl::adjustOwnerCount : nonzero amount input"); + std::uint32_t const current{sle->getFieldU32(sfOwnerCount)}; + AccountID const id = (*sle)[sfAccount]; + std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j); + view.adjustOwnerCountHook(id, current, adjusted); + sle->at(sfOwnerCount) = adjusted; + view.update(sle); +} + +AccountID +pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey) +{ + // This number must not be changed without an amendment + constexpr std::uint16_t maxAccountAttempts = 256; + for (std::uint16_t i = 0; i < maxAccountAttempts; ++i) + { + ripesha_hasher rsh; + auto const hash = sha512Half(i, view.header().parentHash, pseudoOwnerKey); + rsh(hash.data(), hash.size()); + AccountID const ret{static_cast(rsh)}; + if (!view.read(keylet::account(ret))) + return ret; + } + return beast::zero; +} + +// Pseudo-account designator fields MUST be maintained by including the +// SField::sMD_PseudoAccount flag in the SField definition. (Don't forget to +// "| SField::sMD_Default"!) The fields do NOT need to be amendment-gated, +// since a non-active amendment will not set any field, by definition. +// Specific properties of a pseudo-account are NOT checked here, that's what +// InvariantCheck is for. +[[nodiscard]] std::vector const& +getPseudoAccountFields() +{ + static std::vector const pseudoFields = []() { + auto const ar = LedgerFormats::getInstance().findByType(ltACCOUNT_ROOT); + if (!ar) + { + // LCOV_EXCL_START + LogicError( + "xrpl::getPseudoAccountFields : unable to find account root " + "ledger format"); + // LCOV_EXCL_STOP + } + auto const& soTemplate = ar->getSOTemplate(); + + std::vector pseudoFields; + for (auto const& field : soTemplate) + { + if (field.sField().shouldMeta(SField::sMD_PseudoAccount)) + pseudoFields.emplace_back(&field.sField()); + } + return pseudoFields; + }(); + return pseudoFields; +} + +[[nodiscard]] bool +isPseudoAccount( + std::shared_ptr sleAcct, + std::set const& pseudoFieldFilter) +{ + auto const& fields = getPseudoAccountFields(); + + // Intentionally use defensive coding here because it's cheap and makes the + // semantics of true return value clean. + return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT && + std::count_if( + fields.begin(), fields.end(), [&sleAcct, &pseudoFieldFilter](SField const* sf) -> bool { + return sleAcct->isFieldPresent(*sf) && + (pseudoFieldFilter.empty() || pseudoFieldFilter.contains(sf)); + }) > 0; +} + +Expected, TER> +createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField) +{ + [[maybe_unused]] + auto const& fields = getPseudoAccountFields(); + XRPL_ASSERT( + std::count_if( + fields.begin(), + fields.end(), + [&ownerField](SField const* sf) -> bool { return *sf == ownerField; }) == 1, + "xrpl::createPseudoAccount : valid owner field"); + + auto const accountId = pseudoAccountAddress(view, pseudoOwnerKey); + if (accountId == beast::zero) + return Unexpected(tecDUPLICATE); + + // Create pseudo-account. + auto account = std::make_shared(keylet::account(accountId)); + account->setAccountID(sfAccount, accountId); + account->setFieldAmount(sfBalance, STAmount{}); + + // Pseudo-accounts can't submit transactions, so set the sequence number + // to 0 to make them easier to spot and verify, and add an extra level + // of protection. + std::uint32_t const seqno = // + view.rules().enabled(featureSingleAssetVault) || // + view.rules().enabled(featureLendingProtocol) // + ? 0 // + : view.seq(); + account->setFieldU32(sfSequence, seqno); + // Ignore reserves requirement, disable the master key, allow default + // rippling, and enable deposit authorization to prevent payments into + // pseudo-account. + account->setFieldU32(sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth); + // Link the pseudo-account with its owner object. + account->setFieldH256(ownerField, pseudoOwnerKey); + + view.insert(account); + + return account; +} + +[[nodiscard]] TER +checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag) +{ + if (toSle == nullptr) + return tecNO_DST; + + // The tag is basically account-specific information we don't + // understand, but we can require someone to fill it in. + if (toSle->isFlag(lsfRequireDestTag) && !hasDestinationTag) + return tecDST_TAG_NEEDED; // Cannot send without a tag + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/ledger/CredentialHelpers.cpp b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp similarity index 94% rename from src/libxrpl/ledger/CredentialHelpers.cpp rename to src/libxrpl/ledger/helpers/CredentialHelpers.cpp index 04438e09ad..32db285f1e 100644 --- a/src/libxrpl/ledger/CredentialHelpers.cpp +++ b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp @@ -1,5 +1,7 @@ -#include +#include +// #include +#include #include #include @@ -76,7 +78,7 @@ deleteSLE(ApplyView& view, std::shared_ptr const& sleCredential, beast::Jou auto const issuer = sleCredential->getAccountID(sfIssuer); auto const subject = sleCredential->getAccountID(sfSubject); - bool const accepted = sleCredential->getFlags() & lsfAccepted; + bool const accepted = (sleCredential->getFlags() & lsfAccepted) != 0u; auto err = delSLE(issuer, sfIssuerNode, !accepted || (subject == issuer)); if (!isTesSuccess(err)) @@ -145,7 +147,7 @@ valid(STTx const& tx, ReadView const& view, AccountID const& src, beast::Journal return tecBAD_CREDENTIALS; } - if (!(sleCred->getFlags() & lsfAccepted)) + if ((sleCred->getFlags() & lsfAccepted) == 0u) { JLOG(j.trace()) << "Credential isn't accepted. Cred: " << h; return tecBAD_CREDENTIALS; @@ -186,10 +188,12 @@ validDomain(ReadView const& view, uint256 domainID, AccountID const& subject) foundExpired = true; continue; } - else if (sleCredential->getFlags() & lsfAccepted) + if ((sleCredential->getFlags() & lsfAccepted) != 0u) + { return tesSUCCESS; - else - continue; + } + + continue; } } @@ -197,7 +201,7 @@ validDomain(ReadView const& view, uint256 domainID, AccountID const& subject) } TER -authorizedDepositPreauth(ApplyView const& view, STVector256 const& credIDs, AccountID const& dst) +authorizedDepositPreauth(ReadView const& view, STVector256 const& credIDs, AccountID const& dst) { std::set> sorted; std::vector> lifeExtender; @@ -305,7 +309,7 @@ verifyValidDomain(ApplyView& view, AccountID const& account, uint256 domainID, b if (!sleCredential) continue; // expired, i.e. deleted in credentials::removeExpired - if (sleCredential->getFlags() & lsfAccepted) + if ((sleCredential->getFlags() & lsfAccepted) != 0u) return tesSUCCESS; } @@ -318,7 +322,7 @@ verifyDepositPreauth( ApplyView& view, AccountID const& src, AccountID const& dst, - std::shared_ptr const& sleDst, + std::shared_ptr const& sleDst, beast::Journal j) { // If depositPreauth is enabled, then an account that requires @@ -332,14 +336,16 @@ verifyDepositPreauth( if (credentialsPresent && credentials::removeExpired(view, tx.getFieldV256(sfCredentialIDs), j)) return tecEXPIRED; - if (sleDst && (sleDst->getFlags() & lsfDepositAuth)) + if (sleDst && ((sleDst->getFlags() & lsfDepositAuth) != 0u)) { if (src != dst) { if (!view.exists(keylet::depositPreauth(dst, src))) + { return !credentialsPresent ? tecNO_PERMISSION : credentials::authorizedDepositPreauth( view, tx.getFieldV256(sfCredentialIDs), dst); + } } } diff --git a/src/libxrpl/ledger/helpers/DirectoryHelpers.cpp b/src/libxrpl/ledger/helpers/DirectoryHelpers.cpp new file mode 100644 index 0000000000..dba71e0acd --- /dev/null +++ b/src/libxrpl/ledger/helpers/DirectoryHelpers.cpp @@ -0,0 +1,177 @@ +#include +// +#include + +namespace xrpl { + +bool +dirFirst( + ApplyView& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry) +{ + return detail::internalDirFirst(view, root, page, index, entry); +} + +bool +dirNext( + ApplyView& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry) +{ + return detail::internalDirNext(view, root, page, index, entry); +} + +bool +cdirFirst( + ReadView const& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry) +{ + return detail::internalDirFirst(view, root, page, index, entry); +} + +bool +cdirNext( + ReadView const& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry) +{ + return detail::internalDirNext(view, root, page, index, entry); +} + +void +forEachItem( + ReadView const& view, + Keylet const& root, + std::function const&)> const& f) +{ + XRPL_ASSERT(root.type == ltDIR_NODE, "xrpl::forEachItem : valid root type"); + + if (root.type != ltDIR_NODE) + return; + + auto pos = root; + + while (true) + { + auto sle = view.read(pos); + if (!sle) + return; + for (auto const& key : sle->getFieldV256(sfIndexes)) + f(view.read(keylet::child(key))); + auto const next = sle->getFieldU64(sfIndexNext); + if (next == 0u) + return; + pos = keylet::page(root, next); + } +} + +bool +forEachItemAfter( + ReadView const& view, + Keylet const& root, + uint256 const& after, + std::uint64_t const hint, + unsigned int limit, + std::function const&)> const& f) +{ + XRPL_ASSERT(root.type == ltDIR_NODE, "xrpl::forEachItemAfter : valid root type"); + + if (root.type != ltDIR_NODE) + return false; + + auto currentIndex = root; + + // If startAfter is not zero try jumping to that page using the hint + if (after.isNonZero()) + { + auto const hintIndex = keylet::page(root, hint); + + if (auto hintDir = view.read(hintIndex)) + { + for (auto const& key : hintDir->getFieldV256(sfIndexes)) + { + if (key == after) + { + // We found the hint, we can start here + currentIndex = hintIndex; + break; + } + } + } + + bool found = false; + for (;;) + { + auto const ownerDir = view.read(currentIndex); + if (!ownerDir) + return found; + for (auto const& key : ownerDir->getFieldV256(sfIndexes)) + { + if (!found) + { + if (key == after) + found = true; + } + else if (f(view.read(keylet::child(key))) && limit-- <= 1) + { + return found; + } + } + + auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext); + if (uNodeNext == 0) + return found; + currentIndex = keylet::page(root, uNodeNext); + } + } + else + { + for (;;) + { + auto const ownerDir = view.read(currentIndex); + if (!ownerDir) + return true; + for (auto const& key : ownerDir->getFieldV256(sfIndexes)) + { + if (f(view.read(keylet::child(key))) && limit-- <= 1) + return true; + } + auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext); + if (uNodeNext == 0) + return true; + currentIndex = keylet::page(root, uNodeNext); + } + } +} + +bool +dirIsEmpty(ReadView const& view, Keylet const& k) +{ + auto const sleNode = view.read(k); + if (!sleNode) + return true; + if (!sleNode->getFieldV256(sfIndexes).empty()) + return false; + // The first page of a directory may legitimately be empty even if there + // are other pages (the first page is the anchor page) so check to see if + // there is another page. If there is, the directory isn't empty. + return sleNode->getFieldU64(sfIndexNext) == 0; +} + +std::function +describeOwnerDir(AccountID const& account) +{ + return [account](std::shared_ptr const& sle) { (*sle)[sfOwner] = account; }; +} + +} // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp new file mode 100644 index 0000000000..05e4fe1448 --- /dev/null +++ b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp @@ -0,0 +1,773 @@ +#include +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +bool +isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue) +{ + if (auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID()))) + return sle->isFlag(lsfMPTLocked); + return false; +} + +bool +isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue) +{ + if (auto const sle = view.read(keylet::mptoken(mptIssue.getMptID(), account))) + return sle->isFlag(lsfMPTLocked); + return false; +} + +bool +isFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth) +{ + return isGlobalFrozen(view, mptIssue) || isIndividualFrozen(view, account, mptIssue) || + isVaultPseudoAccountFrozen(view, account, mptIssue, depth); +} + +[[nodiscard]] bool +isAnyFrozen( + ReadView const& view, + std::initializer_list const& accounts, + MPTIssue const& mptIssue, + int depth) +{ + if (isGlobalFrozen(view, mptIssue)) + return true; + + for (auto const& account : accounts) + { + if (isIndividualFrozen(view, account, mptIssue)) + return true; + } + + for (auto const& account : accounts) + { + if (isVaultPseudoAccountFrozen(view, account, mptIssue, depth)) + return true; + } + + return false; +} + +Rate +transferRate(ReadView const& view, MPTID const& issuanceID) +{ + // fee is 0-50,000 (0-50%), rate is 1,000,000,000-2,000,000,000 + // For example, if transfer fee is 50% then 10,000 * 50,000 = 500,000 + // which represents 50% of 1,000,000,000 + if (auto const sle = view.read(keylet::mptIssuance(issuanceID)); + sle && sle->isFieldPresent(sfTransferFee)) + return Rate{1'000'000'000u + (10'000 * sle->getFieldU16(sfTransferFee))}; + + return parityRate; +} + +[[nodiscard]] TER +canAddHolding(ReadView const& view, MPTIssue const& mptIssue) +{ + auto mptID = mptIssue.getMptID(); + auto issuance = view.read(keylet::mptIssuance(mptID)); + if (!issuance) + { + return tecOBJECT_NOT_FOUND; + } + if (!issuance->isFlag(lsfMPTCanTransfer)) + { + return tecNO_AUTH; + } + + return tesSUCCESS; +} + +[[nodiscard]] TER +addEmptyHolding( + ApplyView& view, + AccountID const& accountID, + XRPAmount priorBalance, + MPTIssue const& mptIssue, + beast::Journal journal) +{ + auto const& mptID = mptIssue.getMptID(); + auto const mpt = view.peek(keylet::mptIssuance(mptID)); + if (!mpt) + return tefINTERNAL; // LCOV_EXCL_LINE + if (mpt->isFlag(lsfMPTLocked)) + return tefINTERNAL; // LCOV_EXCL_LINE + if (view.peek(keylet::mptoken(mptID, accountID))) + return tecDUPLICATE; + if (accountID == mptIssue.getIssuer()) + return tesSUCCESS; + + return authorizeMPToken(view, priorBalance, mptID, accountID, journal); +} + +[[nodiscard]] TER +authorizeMPToken( + ApplyView& view, + XRPAmount const& priorBalance, + MPTID const& mptIssuanceID, + AccountID const& account, + beast::Journal journal, + std::uint32_t flags, + std::optional holderID) +{ + auto const sleAcct = view.peek(keylet::account(account)); + if (!sleAcct) + return tecINTERNAL; // LCOV_EXCL_LINE + + // If the account that submitted the tx is a holder + // Note: `account_` is holder's account + // `holderID` is NOT used + if (!holderID) + { + // When a holder wants to unauthorize/delete a MPT, the ledger must + // - delete mptokenKey from owner directory + // - delete the MPToken + if ((flags & tfMPTUnauthorize) != 0) + { + auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); + auto const sleMpt = view.peek(mptokenKey); + if (!sleMpt || (*sleMpt)[sfMPTAmount] != 0 || + (view.rules().enabled(fixSecurity3_1_3) && + (*sleMpt)[~sfLockedAmount].value_or(0) != 0)) + return tecINTERNAL; // LCOV_EXCL_LINE + + if (!view.dirRemove( + keylet::ownerDir(account), (*sleMpt)[sfOwnerNode], sleMpt->key(), false)) + return tecINTERNAL; // LCOV_EXCL_LINE + + adjustOwnerCount(view, sleAcct, -1, journal); + + view.erase(sleMpt); + return tesSUCCESS; + } + + // A potential holder wants to authorize/hold a mpt, the ledger must: + // - add the new mptokenKey to the owner directory + // - create the MPToken object for the holder + + // The reserve that is required to create the MPToken. Note + // that although the reserve increases with every item + // an account owns, in the case of MPTokens we only + // *enforce* a reserve if the user owns more than two + // items. This is similar to the reserve requirements of trust lines. + std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount); + XRPAmount const reserveCreate( + (uOwnerCount < 2) ? XRPAmount(beast::zero) + : view.fees().accountReserve(uOwnerCount + 1)); + + if (priorBalance < reserveCreate) + return tecINSUFFICIENT_RESERVE; + + // Defensive check before we attempt to create MPToken for the issuer + auto const mpt = view.read(keylet::mptIssuance(mptIssuanceID)); + if (!mpt || mpt->getAccountID(sfIssuer) == account) + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::authorizeMPToken : invalid issuance or issuers token"); + if (view.rules().enabled(featureLendingProtocol)) + return tecINTERNAL; + // LCOV_EXCL_STOP + } + + auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); + auto mptoken = std::make_shared(mptokenKey); + if (auto ter = dirLink(view, account, mptoken)) + return ter; // LCOV_EXCL_LINE + + (*mptoken)[sfAccount] = account; + (*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID; + (*mptoken)[sfFlags] = 0; + view.insert(mptoken); + + // Update owner count. + adjustOwnerCount(view, sleAcct, 1, journal); + + return tesSUCCESS; + } + + auto const sleMptIssuance = view.read(keylet::mptIssuance(mptIssuanceID)); + if (!sleMptIssuance) + return tecINTERNAL; // LCOV_EXCL_LINE + + // If the account that submitted this tx is the issuer of the MPT + // Note: `account_` is issuer's account + // `holderID` is holder's account + if (account != (*sleMptIssuance)[sfIssuer]) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const sleMpt = view.peek(keylet::mptoken(mptIssuanceID, *holderID)); + if (!sleMpt) + return tecINTERNAL; // LCOV_EXCL_LINE + + std::uint32_t const flagsIn = sleMpt->getFieldU32(sfFlags); + std::uint32_t flagsOut = flagsIn; + + // Issuer wants to unauthorize the holder, unset lsfMPTAuthorized on + // their MPToken + if ((flags & tfMPTUnauthorize) != 0) + { + flagsOut &= ~lsfMPTAuthorized; + } + // Issuer wants to authorize a holder, set lsfMPTAuthorized on their + // MPToken + else + { + flagsOut |= lsfMPTAuthorized; + } + + if (flagsIn != flagsOut) + sleMpt->setFieldU32(sfFlags, flagsOut); + + view.update(sleMpt); + return tesSUCCESS; +} + +[[nodiscard]] TER +removeEmptyHolding( + ApplyView& view, + AccountID const& accountID, + MPTIssue const& mptIssue, + beast::Journal journal) +{ + // If the account is the issuer, then no token should exist. MPTs do not + // have the legacy ability to create such a situation, but check anyway. If + // a token does exist, it will get deleted. If not, return success. + bool const accountIsIssuer = accountID == mptIssue.getIssuer(); + auto const& mptID = mptIssue.getMptID(); + auto const mptoken = view.peek(keylet::mptoken(mptID, accountID)); + if (!mptoken) + return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND; + // Unlike a trust line, if the account is the issuer, and the token has a + // balance, it can not just be deleted, because that will throw the issuance + // accounting out of balance, so fail. Since this should be impossible + // anyway, I'm not going to put any effort into it. + if (mptoken->at(sfMPTAmount) != 0 || + (view.rules().enabled(fixSecurity3_1_3) && (*mptoken)[~sfLockedAmount].value_or(0) != 0)) + return tecHAS_OBLIGATIONS; + + return authorizeMPToken( + view, + {}, // priorBalance + mptID, + accountID, + journal, + tfMPTUnauthorize // flags + ); +} + +[[nodiscard]] TER +requireAuth( + ReadView const& view, + MPTIssue const& mptIssue, + AccountID const& account, + AuthType authType, + int depth) +{ + auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); + auto const sleIssuance = view.read(mptID); + if (!sleIssuance) + return tecOBJECT_NOT_FOUND; + + auto const mptIssuer = sleIssuance->getAccountID(sfIssuer); + + // issuer is always "authorized" + if (mptIssuer == account) // Issuer won't have MPToken + return tesSUCCESS; + + bool const featureSAVEnabled = view.rules().enabled(featureSingleAssetVault); + + if (featureSAVEnabled) + { + if (depth >= maxAssetCheckDepth) + return tecINTERNAL; // LCOV_EXCL_LINE + + // requireAuth is recursive if the issuer is a vault pseudo-account + auto const sleIssuer = view.read(keylet::account(mptIssuer)); + if (!sleIssuer) + return tefINTERNAL; // LCOV_EXCL_LINE + + if (sleIssuer->isFieldPresent(sfVaultID)) + { + auto const sleVault = view.read(keylet::vault(sleIssuer->getFieldH256(sfVaultID))); + if (!sleVault) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const asset = sleVault->at(sfAsset); + if (auto const err = std::visit( + [&](TIss const& issue) { + if constexpr (std::is_same_v) + { + return requireAuth(view, issue, account, authType); + } + else + { + return requireAuth(view, issue, account, authType, depth + 1); + } + }, + asset.value()); + !isTesSuccess(err)) + return err; + } + } + + auto const mptokenID = keylet::mptoken(mptID.key, account); + auto const sleToken = view.read(mptokenID); + + // if account has no MPToken, fail + if (!sleToken && (authType == AuthType::StrongAuth || authType == AuthType::Legacy)) + return tecNO_AUTH; + + // Note, this check is not amendment-gated because DomainID will be always + // empty **unless** writing to it has been enabled by an amendment + auto const maybeDomainID = sleIssuance->at(~sfDomainID); + if (maybeDomainID) + { + XRPL_ASSERT( + sleIssuance->getFieldU32(sfFlags) & lsfMPTRequireAuth, + "xrpl::requireAuth : issuance requires authorization"); + // ter = tefINTERNAL | tecOBJECT_NOT_FOUND | tecNO_AUTH | tecEXPIRED + auto const ter = credentials::validDomain(view, *maybeDomainID, account); + if (isTesSuccess(ter)) + { + return ter; // Note: sleToken might be null + } + if (!sleToken) + { + return ter; + } + // We ignore error from validDomain if we found sleToken, as it could + // belong to someone who is explicitly authorized e.g. a vault owner. + } + + if (featureSAVEnabled) + { + // Implicitly authorize Vault and LoanBroker pseudo-accounts + if (isPseudoAccount(view, account, {&sfVaultID, &sfLoanBrokerID})) + return tesSUCCESS; + } + + // mptoken must be authorized if issuance enabled requireAuth + if (sleIssuance->isFlag(lsfMPTRequireAuth) && + (!sleToken || !sleToken->isFlag(lsfMPTAuthorized))) + return tecNO_AUTH; + + return tesSUCCESS; // Note: sleToken might be null +} + +[[nodiscard]] TER +enforceMPTokenAuthorization( + ApplyView& view, + MPTID const& mptIssuanceID, + AccountID const& account, + XRPAmount const& priorBalance, // for MPToken authorization + beast::Journal j) +{ + auto const sleIssuance = view.read(keylet::mptIssuance(mptIssuanceID)); + if (!sleIssuance) + return tefINTERNAL; // LCOV_EXCL_LINE + + XRPL_ASSERT( + sleIssuance->isFlag(lsfMPTRequireAuth), + "xrpl::enforceMPTokenAuthorization : authorization required"); + + if (account == sleIssuance->at(sfIssuer)) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const keylet = keylet::mptoken(mptIssuanceID, account); + auto const sleToken = view.read(keylet); // NOTE: might be null + auto const maybeDomainID = sleIssuance->at(~sfDomainID); + bool expired = false; + bool const authorizedByDomain = [&]() -> bool { + // NOTE: defensive here, should be checked in preclaim + if (!maybeDomainID.has_value()) + return false; // LCOV_EXCL_LINE + + auto const ter = verifyValidDomain(view, account, *maybeDomainID, j); + if (isTesSuccess(ter)) + return true; + if (ter == tecEXPIRED) + expired = true; + return false; + }(); + + if (!authorizedByDomain && sleToken == nullptr) + { + // Could not find MPToken and won't create one, could be either of: + // + // 1. Field sfDomainID not set in MPTokenIssuance or + // 2. Account has no matching and accepted credentials or + // 3. Account has all expired credentials (deleted in verifyValidDomain) + // + // Either way, return tecNO_AUTH and there is nothing else to do + return expired ? tecEXPIRED : tecNO_AUTH; + } + if (!authorizedByDomain && maybeDomainID.has_value()) + { + // Found an MPToken but the account is not authorized and we expect + // it to have been authorized by the domain. This could be because the + // credentials used to create the MPToken have expired or been deleted. + return expired ? tecEXPIRED : tecNO_AUTH; + } + if (!authorizedByDomain) + { + // We found an MPToken, but sfDomainID is not set, so this is a classic + // MPToken which requires authorization by the token issuer. + XRPL_ASSERT( + sleToken != nullptr && !maybeDomainID.has_value(), + "xrpl::enforceMPTokenAuthorization : found MPToken"); + if (sleToken->isFlag(lsfMPTAuthorized)) + return tesSUCCESS; + + return tecNO_AUTH; + } + if (authorizedByDomain && sleToken != nullptr) + { + // Found an MPToken, authorized by the domain. Ignore authorization flag + // lsfMPTAuthorized because it is meaningless. Return tesSUCCESS + XRPL_ASSERT( + maybeDomainID.has_value(), + "xrpl::enforceMPTokenAuthorization : found MPToken for domain"); + return tesSUCCESS; + } + if (authorizedByDomain) + { + // Could not find MPToken but there should be one because we are + // authorized by domain. Proceed to create it, then return tesSUCCESS + XRPL_ASSERT( + maybeDomainID.has_value() && sleToken == nullptr, + "xrpl::enforceMPTokenAuthorization : new MPToken for domain"); + if (auto const err = authorizeMPToken( + view, + priorBalance, // priorBalance + mptIssuanceID, // mptIssuanceID + account, // account + j); + !isTesSuccess(err)) + return err; + + return tesSUCCESS; + } + + // LCOV_EXCL_START + UNREACHABLE("xrpl::enforceMPTokenAuthorization : condition list is incomplete"); + return tefINTERNAL; + // LCOV_EXCL_STOP +} + +TER +canTransfer( + ReadView const& view, + MPTIssue const& mptIssue, + AccountID const& from, + AccountID const& to) +{ + auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); + auto const sleIssuance = view.read(mptID); + if (!sleIssuance) + return tecOBJECT_NOT_FOUND; + + if (!sleIssuance->isFlag(lsfMPTCanTransfer)) + { + if (from != (*sleIssuance)[sfIssuer] && to != (*sleIssuance)[sfIssuer]) + return TER{tecNO_AUTH}; + } + return tesSUCCESS; +} + +TER +lockEscrowMPT(ApplyView& view, AccountID const& sender, STAmount const& amount, beast::Journal j) +{ + auto const mptIssue = amount.get(); + auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); + auto sleIssuance = view.peek(mptID); + if (!sleIssuance) + { // LCOV_EXCL_START + JLOG(j.error()) << "lockEscrowMPT: MPT issuance not found for " << mptIssue.getMptID(); + return tecOBJECT_NOT_FOUND; + } // LCOV_EXCL_STOP + + if (amount.getIssuer() == sender) + { // LCOV_EXCL_START + JLOG(j.error()) << "lockEscrowMPT: sender is the issuer, cannot lock MPTs."; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + // 1. Decrease the MPT Holder MPTAmount + // 2. Increase the MPT Holder EscrowedAmount + { + auto const mptokenID = keylet::mptoken(mptID.key, sender); + auto sle = view.peek(mptokenID); + if (!sle) + { // LCOV_EXCL_START + JLOG(j.error()) << "lockEscrowMPT: MPToken not found for " << sender; + return tecOBJECT_NOT_FOUND; + } // LCOV_EXCL_STOP + + auto const amt = sle->getFieldU64(sfMPTAmount); + auto const pay = amount.mpt().value(); + + // Underflow check for subtraction + if (!canSubtract(STAmount(mptIssue, amt), STAmount(mptIssue, pay))) + { // LCOV_EXCL_START + JLOG(j.error()) << "lockEscrowMPT: insufficient MPTAmount for " << to_string(sender) + << ": " << amt << " < " << pay; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + (*sle)[sfMPTAmount] = amt - pay; + + // Overflow check for addition + uint64_t const locked = (*sle)[~sfLockedAmount].value_or(0); + + if (!canAdd(STAmount(mptIssue, locked), STAmount(mptIssue, pay))) + { // LCOV_EXCL_START + JLOG(j.error()) << "lockEscrowMPT: overflow on locked amount for " << to_string(sender) + << ": " << locked << " + " << pay; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + if (sle->isFieldPresent(sfLockedAmount)) + { + (*sle)[sfLockedAmount] += pay; + } + else + { + sle->setFieldU64(sfLockedAmount, pay); + } + + view.update(sle); + } + + // 1. Increase the Issuance EscrowedAmount + // 2. DO NOT change the Issuance OutstandingAmount + { + uint64_t const issuanceEscrowed = (*sleIssuance)[~sfLockedAmount].value_or(0); + auto const pay = amount.mpt().value(); + + // Overflow check for addition + if (!canAdd(STAmount(mptIssue, issuanceEscrowed), STAmount(mptIssue, pay))) + { // LCOV_EXCL_START + JLOG(j.error()) << "lockEscrowMPT: overflow on issuance " + "locked amount for " + << mptIssue.getMptID() << ": " << issuanceEscrowed << " + " << pay; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + if (sleIssuance->isFieldPresent(sfLockedAmount)) + { + (*sleIssuance)[sfLockedAmount] += pay; + } + else + { + sleIssuance->setFieldU64(sfLockedAmount, pay); + } + + view.update(sleIssuance); + } + return tesSUCCESS; +} + +TER +unlockEscrowMPT( + ApplyView& view, + AccountID const& sender, + AccountID const& receiver, + STAmount const& netAmount, + STAmount const& grossAmount, + beast::Journal j) +{ + if (!view.rules().enabled(fixTokenEscrowV1)) + { + XRPL_ASSERT(netAmount == grossAmount, "xrpl::unlockEscrowMPT : netAmount == grossAmount"); + } + + auto const& issuer = netAmount.getIssuer(); + auto const& mptIssue = netAmount.get(); + auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); + auto sleIssuance = view.peek(mptID); + if (!sleIssuance) + { // LCOV_EXCL_START + JLOG(j.error()) << "unlockEscrowMPT: MPT issuance not found for " << mptIssue.getMptID(); + return tecOBJECT_NOT_FOUND; + } // LCOV_EXCL_STOP + + // Decrease the Issuance EscrowedAmount + { + if (!sleIssuance->isFieldPresent(sfLockedAmount)) + { // LCOV_EXCL_START + JLOG(j.error()) << "unlockEscrowMPT: no locked amount in issuance for " + << mptIssue.getMptID(); + return tecINTERNAL; + } // LCOV_EXCL_STOP + + auto const locked = sleIssuance->getFieldU64(sfLockedAmount); + auto const redeem = grossAmount.mpt().value(); + + // Underflow check for subtraction + if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, redeem))) + { // LCOV_EXCL_START + JLOG(j.error()) << "unlockEscrowMPT: insufficient locked amount for " + << mptIssue.getMptID() << ": " << locked << " < " << redeem; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + auto const newLocked = locked - redeem; + if (newLocked == 0) + { + sleIssuance->makeFieldAbsent(sfLockedAmount); + } + else + { + sleIssuance->setFieldU64(sfLockedAmount, newLocked); + } + view.update(sleIssuance); + } + + if (issuer != receiver) + { + // Increase the MPT Holder MPTAmount + auto const mptokenID = keylet::mptoken(mptID.key, receiver); + auto sle = view.peek(mptokenID); + if (!sle) + { // LCOV_EXCL_START + JLOG(j.error()) << "unlockEscrowMPT: MPToken not found for " << receiver; + return tecOBJECT_NOT_FOUND; + } // LCOV_EXCL_STOP + + auto current = sle->getFieldU64(sfMPTAmount); + auto delta = netAmount.mpt().value(); + + // Overflow check for addition + if (!canAdd(STAmount(mptIssue, current), STAmount(mptIssue, delta))) + { // LCOV_EXCL_START + JLOG(j.error()) << "unlockEscrowMPT: overflow on MPTAmount for " << to_string(receiver) + << ": " << current << " + " << delta; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + (*sle)[sfMPTAmount] += delta; + view.update(sle); + } + else + { + // Decrease the Issuance OutstandingAmount + auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount); + auto const redeem = netAmount.mpt().value(); + + // Underflow check for subtraction + if (!canSubtract(STAmount(mptIssue, outstanding), STAmount(mptIssue, redeem))) + { // LCOV_EXCL_START + JLOG(j.error()) << "unlockEscrowMPT: insufficient outstanding amount for " + << mptIssue.getMptID() << ": " << outstanding << " < " << redeem; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem); + view.update(sleIssuance); + } + + if (issuer == sender) + { // LCOV_EXCL_START + JLOG(j.error()) << "unlockEscrowMPT: sender is the issuer, " + "cannot unlock MPTs."; + return tecINTERNAL; + } // LCOV_EXCL_STOP + // Decrease the MPT Holder EscrowedAmount + auto const mptokenID = keylet::mptoken(mptID.key, sender); + auto sle = view.peek(mptokenID); + if (!sle) + { // LCOV_EXCL_START + JLOG(j.error()) << "unlockEscrowMPT: MPToken not found for " << sender; + return tecOBJECT_NOT_FOUND; + } // LCOV_EXCL_STOP + + if (!sle->isFieldPresent(sfLockedAmount)) + { // LCOV_EXCL_START + JLOG(j.error()) << "unlockEscrowMPT: no locked amount in MPToken for " << to_string(sender); + return tecINTERNAL; + } // LCOV_EXCL_STOP + + auto const locked = sle->getFieldU64(sfLockedAmount); + auto const delta = grossAmount.mpt().value(); + + // Underflow check for subtraction + if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, delta))) + { // LCOV_EXCL_START + JLOG(j.error()) << "unlockEscrowMPT: insufficient locked amount for " << to_string(sender) + << ": " << locked << " < " << delta; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + auto const newLocked = locked - delta; + if (newLocked == 0) + { + sle->makeFieldAbsent(sfLockedAmount); + } + else + { + sle->setFieldU64(sfLockedAmount, newLocked); + } + view.update(sle); + + // Note: The gross amount is the amount that was locked, the net + // amount is the amount that is being unlocked. The difference is the fee + // that was charged for the transfer. If this difference is greater than + // zero, we need to update the outstanding amount. + auto const diff = grossAmount.mpt().value() - netAmount.mpt().value(); + if (diff != 0) + { + auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount); + // Underflow check for subtraction + if (!canSubtract(STAmount(mptIssue, outstanding), STAmount(mptIssue, diff))) + { // LCOV_EXCL_START + JLOG(j.error()) << "unlockEscrowMPT: insufficient outstanding amount for " + << mptIssue.getMptID() << ": " << outstanding << " < " << diff; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - diff); + view.update(sleIssuance); + } + return tesSUCCESS; +} + +TER +createMPToken( + ApplyView& view, + MPTID const& mptIssuanceID, + AccountID const& account, + std::uint32_t const flags) +{ + auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); + + auto const ownerNode = + view.dirInsert(keylet::ownerDir(account), mptokenKey, describeOwnerDir(account)); + + if (!ownerNode) + return tecDIR_FULL; // LCOV_EXCL_LINE + + auto mptoken = std::make_shared(mptokenKey); + (*mptoken)[sfAccount] = account; + (*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID; + (*mptoken)[sfFlags] = flags; + (*mptoken)[sfOwnerNode] = *ownerNode; + + view.insert(mptoken); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/nft/NFTokenUtils.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp similarity index 94% rename from src/libxrpl/tx/transactors/nft/NFTokenUtils.cpp rename to src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index 650fdabf29..d6921b5c88 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenUtils.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -1,11 +1,16 @@ +#include #include #include #include +#include +#include +#include +#include +#include #include #include #include #include -#include #include #include @@ -60,7 +65,7 @@ getPageForToken( // A suitable page doesn't exist; we'll have to create one. if (!cp) { - STArray arr; + STArray const arr; cp = std::make_shared(last); cp->setFieldArray(sfNFTokens, arr); view.insert(cp); @@ -101,9 +106,11 @@ getPageForToken( // equivalent NFTokens then check the front of the page for a // place to make the split. if (splitIter == narr.end()) + { splitIter = std::find_if(narr.begin(), narr.end(), [&cmp](STObject const& obj) { return (obj.getFieldH256(sfNFTokenID) & nft::pageMask) == cmp; }); + } // There should be no circumstance when splitIter == end(), but if it // were to happen we should bail out because something is confused. @@ -218,9 +225,13 @@ changeTokenURI( return tecINTERNAL; // LCOV_EXCL_LINE if (uri) + { nftIter->setFieldVL(sfURI, *uri); + } else if (nftIter->isFieldPresent(sfURI)) + { nftIter->makeFieldAbsent(sfURI); + } view.update(page); return tesSUCCESS; @@ -235,7 +246,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 page = + std::shared_ptr const page = getPageForToken(view, owner, nft[sfNFTokenID], [](ApplyView& view, AccountID const& owner) { adjustOwnerCount( view, @@ -328,13 +339,13 @@ mergePages(ApplyView& view, std::shared_ptr const& p1, std::shared_ptr TER removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID) { - std::shared_ptr page = locatePage(view, owner, nftokenID); + std::shared_ptr const page = locatePage(view, owner, nftokenID); // If the page couldn't be found, the given NFT isn't owned by this account if (!page) return tecNO_ENTRY; - return removeToken(view, owner, nftokenID, std::move(page)); + return removeToken(view, owner, nftokenID, page); } /** Remove the token from the owner's token directory. */ @@ -343,7 +354,7 @@ removeToken( ApplyView& view, AccountID const& owner, uint256 const& nftokenID, - std::shared_ptr&& curr) + std::shared_ptr const& curr) { // We found a page, but the given NFT may not be in it. auto arr = curr->getFieldArray(sfNFTokens); @@ -368,9 +379,11 @@ removeToken( page2 = view.peek(Keylet(ltNFTOKEN_PAGE, *id)); if (!page2) + { Throw( "page " + to_string(page1->key()) + " has a broken " + field.getName() + " field pointing to " + to_string(*id)); + } } return page2; @@ -396,11 +409,13 @@ removeToken( cnt--; if (cnt != 0) + { adjustOwnerCount( view, view.peek(keylet::account(owner)), cnt, beast::Journal{beast::Journal::getNullSink()}); + } return tesSUCCESS; } @@ -448,9 +463,13 @@ removeToken( // The page is empty and not the last page, so we can just unlink it // and then remove it. if (next) + { prev->setFieldH256(sfNextPageMin, next->key()); + } else + { prev->makeFieldAbsent(sfNextPageMin); + } view.update(prev); } @@ -459,9 +478,13 @@ removeToken( { // Make our next page point to our previous page: if (prev) + { next->setFieldH256(sfPreviousPageMin, prev->key()); + } else + { next->makeFieldAbsent(sfPreviousPageMin); + } view.update(next); } @@ -497,7 +520,7 @@ removeToken( std::optional findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID) { - std::shared_ptr page = locatePage(view, owner, nftokenID); + std::shared_ptr const page = locatePage(view, owner, nftokenID); // If the page couldn't be found, the given NFT isn't owned by this account if (!page) @@ -526,8 +549,10 @@ findTokenAndPage(ApplyView& view, AccountID const& owner, uint256 const& nftoken for (auto const& t : page->getFieldArray(sfNFTokens)) { if (t[sfNFTokenID] == nftokenID) + { // This std::optional constructor is explicit, so it is spelled out. return std::optional(std::in_place, t, std::move(page)); + } } return std::nullopt; } @@ -564,16 +589,20 @@ removeTokenOffersWithLimit(ApplyView& view, Keylet const& directory, std::size_t if (auto const offer = view.peek(keylet::nftoffer(offerIndexes[i]))) { if (deleteTokenOffer(view, offer)) + { ++deletedOffersCount; + } else + { Throw( "Offer " + to_string(offerIndexes[i]) + " cannot be deleted!"); + } } if (maxDeletableOffers == deletedOffersCount) break; } - } while (pageIndex.value_or(0) && maxDeletableOffers != deletedOffersCount); + } while ((pageIndex.value_or(0) != 0u) && maxDeletableOffers != deletedOffersCount); return deletedOffersCount; } @@ -584,7 +613,7 @@ notTooManyOffers(ReadView const& view, uint256 const& nftokenID) std::size_t totalOffers = 0; { - Dir buys(view, keylet::nft_buys(nftokenID)); + Dir const buys(view, keylet::nft_buys(nftokenID)); for (auto iter = buys.begin(); iter != buys.end(); iter.next_page()) { totalOffers += iter.page_size(); @@ -594,7 +623,7 @@ notTooManyOffers(ReadView const& view, uint256 const& nftokenID) } { - Dir sells(view, keylet::nft_sells(nftokenID)); + Dir const sells(view, keylet::nft_sells(nftokenID)); for (auto iter = sells.begin(); iter != sells.end(); iter.next_page()) { totalOffers += iter.page_size(); @@ -619,8 +648,8 @@ deleteTokenOffer(ApplyView& view, std::shared_ptr const& offer) auto const nftokenID = (*offer)[sfNFTokenID]; if (!view.dirRemove( - ((*offer)[sfFlags] & tfSellNFToken) ? keylet::nft_sells(nftokenID) - : keylet::nft_buys(nftokenID), + (((*offer)[sfFlags] & lsfSellNFToken) != 0u) ? keylet::nft_sells(nftokenID) + : keylet::nft_buys(nftokenID), (*offer)[sfNFTokenOfferNode], offer->key(), false)) @@ -696,8 +725,10 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner) } if (nextPage->key() == last.key) + { // We need special handling for the last page. break; + } page = nextPage; } @@ -727,9 +758,11 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner) // Also fix up the NextPageMin link in the new Previous. auto const newPrev = view.peek(Keylet(ltNFTOKEN_PAGE, *prevLink)); if (!newPrev) + { Throw( "NFTokenPage directory for " + to_string(owner) + " cannot be repaired. Unexpected link problem."); + } newPrev->at(sfNextPageMin) = nextPage->key(); view.update(newPrev); } @@ -760,12 +793,14 @@ tokenOfferCreatePreflight( std::uint32_t txFlags) { if (amount.negative()) + { // An offer for a negative amount makes no sense. return temBAD_AMOUNT; + } if (!isXRP(amount)) { - if (nftFlags & nft::flagOnlyXRP) + if ((nftFlags & nft::flagOnlyXRP) != 0) return temBAD_AMOUNT; if (!amount) @@ -774,7 +809,7 @@ tokenOfferCreatePreflight( // If this is an offer to buy, you must offer something; if it's an // offer to sell, you can ask for nothing. - bool const isSellOffer = txFlags & tfSellNFToken; + bool const isSellOffer = (txFlags & tfSellNFToken) != 0u; if (!isSellOffer && !amount) return temBAD_AMOUNT; @@ -810,7 +845,7 @@ tokenOfferCreatePreclaim( std::optional const& owner, std::uint32_t txFlags) { - if (!(nftFlags & nft::flagCreateTrustLines) && !amount.native() && xferFee) + if (((nftFlags & nft::flagCreateTrustLines) == 0) && !amount.native() && (xferFee != 0u)) { if (!view.exists(keylet::account(nftIssuer))) return tecNO_ISSUER; @@ -832,7 +867,7 @@ tokenOfferCreatePreclaim( return tecFROZEN; } - if (nftIssuer != acctID && !(nftFlags & nft::flagTransferable)) + if (nftIssuer != acctID && ((nftFlags & nft::flagTransferable) == 0)) { auto const root = view.read(keylet::account(nftIssuer)); XRPL_ASSERT(root, "xrpl::nft::tokenOfferCreatePreclaim : non-null account"); @@ -865,7 +900,7 @@ tokenOfferCreatePreclaim( return tecNO_DST; // check if the destination has disallowed incoming offers - if (sleDst->getFlags() & lsfDisallowIncomingNFTokenOffer) + if ((sleDst->getFlags() & lsfDisallowIncomingNFTokenOffer) != 0u) return tecNO_PERMISSION; } @@ -878,7 +913,7 @@ tokenOfferCreatePreclaim( if (!sleOwner) return tecNO_TARGET; - if (sleOwner->getFlags() & lsfDisallowIncomingNFTokenOffer) + if ((sleOwner->getFlags() & lsfDisallowIncomingNFTokenOffer) != 0u) return tecNO_PERMISSION; } @@ -891,7 +926,7 @@ tokenOfferCreatePreclaim( // unauthorized trustlines with balance auto const res = nft::checkTrustlineAuthorized(view, acctID, j, amount.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; } return tesSUCCESS; @@ -926,7 +961,7 @@ tokenOfferCreateApply( if (!ownerNode) return tecDIR_FULL; // LCOV_EXCL_LINE - bool const isSellOffer = txFlags & tfSellNFToken; + bool const isSellOffer = (txFlags & tfSellNFToken) != 0u; // Token offers are also added to the token's buy or sell offer // directory @@ -1060,7 +1095,8 @@ checkTrustlineDeepFrozen( // There's no difference which side enacted deep freeze, accepting // tokens shouldn't be possible. - bool const deepFrozen = (*trustLine)[sfFlags] & (lsfLowDeepFreeze | lsfHighDeepFreeze); + bool const deepFrozen = + ((*trustLine)[sfFlags] & (lsfLowDeepFreeze | lsfHighDeepFreeze)) != 0u; if (deepFrozen) { diff --git a/src/libxrpl/ledger/helpers/OfferHelpers.cpp b/src/libxrpl/ledger/helpers/OfferHelpers.cpp new file mode 100644 index 0000000000..3d63240fd0 --- /dev/null +++ b/src/libxrpl/ledger/helpers/OfferHelpers.cpp @@ -0,0 +1,58 @@ +#include +// +#include +#include +#include +#include + +namespace xrpl { + +TER +offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j) +{ + if (!sle) + return tesSUCCESS; + auto offerIndex = sle->key(); + auto owner = sle->getAccountID(sfAccount); + + // Detect legacy directories. + uint256 const uDirectory = sle->getFieldH256(sfBookDirectory); + + if (!view.dirRemove(keylet::ownerDir(owner), sle->getFieldU64(sfOwnerNode), offerIndex, false)) + { + return tefBAD_LEDGER; // LCOV_EXCL_LINE + } + + if (!view.dirRemove(keylet::page(uDirectory), sle->getFieldU64(sfBookNode), offerIndex, false)) + { + return tefBAD_LEDGER; // LCOV_EXCL_LINE + } + + if (sle->isFieldPresent(sfAdditionalBooks)) + { + XRPL_ASSERT( + sle->isFlag(lsfHybrid) && sle->isFieldPresent(sfDomainID), + "xrpl::offerDelete : should be a hybrid domain offer"); + + auto const& additionalBookDirs = sle->getFieldArray(sfAdditionalBooks); + + for (auto const& bookDir : additionalBookDirs) + { + auto const& dirIndex = bookDir.getFieldH256(sfBookDirectory); + auto const& dirNode = bookDir.getFieldU64(sfBookNode); + + if (!view.dirRemove(keylet::page(dirIndex), dirNode, offerIndex, false)) + { + return tefBAD_LEDGER; // LCOV_EXCL_LINE + } + } + } + + adjustOwnerCount(view, view.peek(keylet::account(owner)), -1, j); + + view.erase(sle); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp b/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp new file mode 100644 index 0000000000..a9fab07194 --- /dev/null +++ b/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include + +namespace xrpl { + +TER +closeChannel( + std::shared_ptr const& slep, + ApplyView& view, + uint256 const& key, + beast::Journal j) +{ + AccountID const src = (*slep)[sfAccount]; + // Remove PayChan from owner directory + { + auto const page = (*slep)[sfOwnerNode]; + if (!view.dirRemove(keylet::ownerDir(src), page, key, true)) + { + // LCOV_EXCL_START + JLOG(j.fatal()) << "Could not remove paychan from src owner directory"; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + + // Remove PayChan from recipient's owner directory, if present. + if (auto const page = (*slep)[~sfDestinationNode]) + { + auto const dst = (*slep)[sfDestination]; + if (!view.dirRemove(keylet::ownerDir(dst), *page, key, true)) + { + // LCOV_EXCL_START + JLOG(j.fatal()) << "Could not remove paychan from dst owner directory"; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + + // Transfer amount back to owner, decrement owner count + auto const sle = view.peek(keylet::account(src)); + if (!sle) + return tefINTERNAL; // LCOV_EXCL_LINE + + XRPL_ASSERT( + (*slep)[sfAmount] >= (*slep)[sfBalance], "xrpl::closeChannel : minimum channel amount"); + (*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance]; + adjustOwnerCount(view, sle, -1, j); + view.update(sle); + + // Remove PayChan from ledger + view.erase(slep); + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/dex/PermissionedDEXHelpers.cpp b/src/libxrpl/ledger/helpers/PermissionedDEXHelpers.cpp similarity index 94% rename from src/libxrpl/tx/transactors/dex/PermissionedDEXHelpers.cpp rename to src/libxrpl/ledger/helpers/PermissionedDEXHelpers.cpp index 37db2d632f..4b2bde19f8 100644 --- a/src/libxrpl/tx/transactors/dex/PermissionedDEXHelpers.cpp +++ b/src/libxrpl/ledger/helpers/PermissionedDEXHelpers.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include namespace xrpl { namespace permissioned_dex { diff --git a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp new file mode 100644 index 0000000000..697b8fc293 --- /dev/null +++ b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp @@ -0,0 +1,760 @@ +#include +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +//------------------------------------------------------------------------------ +// +// Credit functions (from Credit.cpp) +// +//------------------------------------------------------------------------------ + +STAmount +creditLimit( + ReadView const& view, + AccountID const& account, + AccountID const& issuer, + Currency const& currency) +{ + STAmount result(Issue{currency, account}); + + auto sleRippleState = view.read(keylet::line(account, issuer, currency)); + + if (sleRippleState) + { + result = sleRippleState->getFieldAmount(account < issuer ? sfLowLimit : sfHighLimit); + result.setIssuer(account); + } + + XRPL_ASSERT(result.getIssuer() == account, "xrpl::creditLimit : result issuer match"); + XRPL_ASSERT(result.getCurrency() == currency, "xrpl::creditLimit : result currency match"); + return result; +} + +IOUAmount +creditLimit2(ReadView const& v, AccountID const& acc, AccountID const& iss, Currency const& cur) +{ + return toAmount(creditLimit(v, acc, iss, cur)); +} + +STAmount +creditBalance( + ReadView const& view, + AccountID const& account, + AccountID const& issuer, + Currency const& currency) +{ + STAmount result(Issue{currency, account}); + + auto sleRippleState = view.read(keylet::line(account, issuer, currency)); + + if (sleRippleState) + { + result = sleRippleState->getFieldAmount(sfBalance); + if (account < issuer) + result.negate(); + result.setIssuer(account); + } + + XRPL_ASSERT(result.getIssuer() == account, "xrpl::creditBalance : result issuer match"); + XRPL_ASSERT(result.getCurrency() == currency, "xrpl::creditBalance : result currency match"); + return result; +} + +//------------------------------------------------------------------------------ +// +// Freeze checking (IOU-specific) +// +//------------------------------------------------------------------------------ + +bool +isIndividualFrozen( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer) +{ + if (isXRP(currency)) + return false; + if (issuer != account) + { + // Check if the issuer froze the line + auto const sle = view.read(keylet::line(account, issuer, currency)); + if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze)) + return true; + } + return false; +} + +// Can the specified account spend the specified currency issued by +// the specified issuer or does the freeze flag prohibit it? +bool +isFrozen( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer) +{ + if (isXRP(currency)) + return false; + auto sle = view.read(keylet::account(issuer)); + if (sle && sle->isFlag(lsfGlobalFreeze)) + return true; + if (issuer != account) + { + // Check if the issuer froze the line + sle = view.read(keylet::line(account, issuer, currency)); + if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze)) + return true; + } + return false; +} + +bool +isDeepFrozen( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer) +{ + if (isXRP(currency)) + { + return false; + } + + if (issuer == account) + { + return false; + } + + auto const sle = view.read(keylet::line(account, issuer, currency)); + if (!sle) + { + return false; + } + + return sle->isFlag(lsfHighDeepFreeze) || sle->isFlag(lsfLowDeepFreeze); +} + +//------------------------------------------------------------------------------ +// +// Trust line operations +// +//------------------------------------------------------------------------------ + +TER +trustCreate( + ApplyView& view, + bool const bSrcHigh, + AccountID const& uSrcAccountID, + AccountID const& uDstAccountID, + uint256 const& uIndex, // --> ripple state entry + SLE::ref sleAccount, // --> the account being set. + bool const bAuth, // --> authorize account. + bool const bNoRipple, // --> others cannot ripple through + bool const bFreeze, // --> funds cannot leave + bool bDeepFreeze, // --> can neither receive nor send funds + STAmount const& saBalance, // --> balance of account being set. + // Issuer should be noAccount() + STAmount const& saLimit, // --> limit for account being set. + // Issuer should be the account being set. + std::uint32_t uQualityIn, + std::uint32_t uQualityOut, + beast::Journal j) +{ + JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", " + << to_string(uDstAccountID) << ", " << saBalance.getFullText(); + + auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID; + auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID; + if (uLowAccountID == uHighAccountID) + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::trustCreate : trust line to self"); + if (view.rules().enabled(featureLendingProtocol)) + return tecINTERNAL; + // LCOV_EXCL_STOP + } + + auto const sleRippleState = std::make_shared(ltRIPPLE_STATE, uIndex); + view.insert(sleRippleState); + + auto lowNode = view.dirInsert( + keylet::ownerDir(uLowAccountID), sleRippleState->key(), describeOwnerDir(uLowAccountID)); + + if (!lowNode) + return tecDIR_FULL; // LCOV_EXCL_LINE + + auto highNode = view.dirInsert( + keylet::ownerDir(uHighAccountID), sleRippleState->key(), describeOwnerDir(uHighAccountID)); + + if (!highNode) + return tecDIR_FULL; // LCOV_EXCL_LINE + + bool const bSetDst = saLimit.getIssuer() == uDstAccountID; + bool const bSetHigh = bSrcHigh ^ bSetDst; + + XRPL_ASSERT(sleAccount, "xrpl::trustCreate : non-null SLE"); + if (!sleAccount) + return tefINTERNAL; // LCOV_EXCL_LINE + + XRPL_ASSERT( + sleAccount->getAccountID(sfAccount) == (bSetHigh ? uHighAccountID : uLowAccountID), + "xrpl::trustCreate : matching account ID"); + auto const slePeer = view.peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID)); + if (!slePeer) + return tecNO_TARGET; + + // Remember deletion hints. + sleRippleState->setFieldU64(sfLowNode, *lowNode); + sleRippleState->setFieldU64(sfHighNode, *highNode); + + sleRippleState->setFieldAmount(bSetHigh ? sfHighLimit : sfLowLimit, saLimit); + sleRippleState->setFieldAmount( + bSetHigh ? sfLowLimit : sfHighLimit, + STAmount(Issue{saBalance.getCurrency(), bSetDst ? uSrcAccountID : uDstAccountID})); + + if (uQualityIn != 0u) + sleRippleState->setFieldU32(bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn); + + if (uQualityOut != 0u) + sleRippleState->setFieldU32(bSetHigh ? sfHighQualityOut : sfLowQualityOut, uQualityOut); + + std::uint32_t uFlags = bSetHigh ? lsfHighReserve : lsfLowReserve; + + if (bAuth) + { + uFlags |= (bSetHigh ? lsfHighAuth : lsfLowAuth); + } + if (bNoRipple) + { + uFlags |= (bSetHigh ? lsfHighNoRipple : lsfLowNoRipple); + } + if (bFreeze) + { + uFlags |= (bSetHigh ? lsfHighFreeze : lsfLowFreeze); + } + if (bDeepFreeze) + { + uFlags |= (bSetHigh ? lsfHighDeepFreeze : lsfLowDeepFreeze); + } + + if ((slePeer->getFlags() & lsfDefaultRipple) == 0) + { + // The other side's default is no rippling + uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple); + } + + sleRippleState->setFieldU32(sfFlags, uFlags); + adjustOwnerCount(view, sleAccount, 1, j); + + // ONLY: Create ripple balance. + sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance); + + view.creditHook(uSrcAccountID, uDstAccountID, saBalance, saBalance.zeroed()); + + return tesSUCCESS; +} + +TER +trustDelete( + ApplyView& view, + std::shared_ptr const& sleRippleState, + AccountID const& uLowAccountID, + AccountID const& uHighAccountID, + beast::Journal j) +{ + // Detect legacy dirs. + std::uint64_t const uLowNode = sleRippleState->getFieldU64(sfLowNode); + std::uint64_t const uHighNode = sleRippleState->getFieldU64(sfHighNode); + + JLOG(j.trace()) << "trustDelete: Deleting ripple line: low"; + + if (!view.dirRemove(keylet::ownerDir(uLowAccountID), uLowNode, sleRippleState->key(), false)) + { + return tefBAD_LEDGER; // LCOV_EXCL_LINE + } + + JLOG(j.trace()) << "trustDelete: Deleting ripple line: high"; + + if (!view.dirRemove(keylet::ownerDir(uHighAccountID), uHighNode, sleRippleState->key(), false)) + { + return tefBAD_LEDGER; // LCOV_EXCL_LINE + } + + JLOG(j.trace()) << "trustDelete: Deleting ripple line: state"; + view.erase(sleRippleState); + + return tesSUCCESS; +} + +//------------------------------------------------------------------------------ +// +// IOU issuance/redemption +// +//------------------------------------------------------------------------------ + +static bool +updateTrustLine( + ApplyView& view, + SLE::pointer state, + bool bSenderHigh, + AccountID const& sender, + STAmount const& before, + STAmount const& after, + beast::Journal j) +{ + if (!state) + return false; + std::uint32_t const flags(state->getFieldU32(sfFlags)); + + auto sle = view.peek(keylet::account(sender)); + if (!sle) + return false; + + // YYY Could skip this if rippling in reverse. + if (before > beast::zero + // Sender balance was positive. + && after <= beast::zero + // Sender is zero or negative. + && ((flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) != 0u) + // Sender reserve is set. + && static_cast(flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != + static_cast(sle->getFlags() & lsfDefaultRipple) && + ((flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) == 0u) && + !state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit) + // Sender trust limit is 0. + && (state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) == 0u) + // Sender quality in is 0. + && (state->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut) == 0u)) + // Sender quality out is 0. + { + // VFALCO Where is the line being deleted? + // Clear the reserve of the sender, possibly delete the line! + adjustOwnerCount(view, sle, -1, j); + + // Clear reserve flag. + state->setFieldU32(sfFlags, flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); + + // Balance is zero, receiver reserve is clear. + if (!after // Balance is zero. + && ((flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)) == 0u)) + return true; + } + return false; +} + +TER +issueIOU( + ApplyView& view, + AccountID const& account, + STAmount const& amount, + Issue const& issue, + beast::Journal j) +{ + XRPL_ASSERT( + !isXRP(account) && !isXRP(issue.account), + "xrpl::issueIOU : neither account nor issuer is XRP"); + + // Consistency check + XRPL_ASSERT(issue == amount.issue(), "xrpl::issueIOU : matching issue"); + + // Can't send to self! + XRPL_ASSERT(issue.account != account, "xrpl::issueIOU : not issuer account"); + + JLOG(j.trace()) << "issueIOU: " << to_string(account) << ": " << amount.getFullText(); + + bool const bSenderHigh = issue.account > account; + + auto const index = keylet::line(issue.account, account, issue.currency); + + if (auto state = view.peek(index)) + { + STAmount final_balance = state->getFieldAmount(sfBalance); + + if (bSenderHigh) + final_balance.negate(); // Put balance in sender terms. + + STAmount const start_balance = final_balance; + + final_balance -= amount; + + auto const must_delete = updateTrustLine( + view, state, bSenderHigh, issue.account, start_balance, final_balance, j); + + view.creditHook(issue.account, account, amount, start_balance); + + if (bSenderHigh) + final_balance.negate(); + + // Adjust the balance on the trust line if necessary. We do this even + // if we are going to delete the line to reflect the correct balance + // at the time of deletion. + state->setFieldAmount(sfBalance, final_balance); + if (must_delete) + { + return trustDelete( + view, + state, + bSenderHigh ? account : issue.account, + bSenderHigh ? issue.account : account, + j); + } + + view.update(state); + + return tesSUCCESS; + } + + // NIKB TODO: The limit uses the receiver's account as the issuer and + // this is unnecessarily inefficient as copying which could be avoided + // is now required. Consider available options. + STAmount const limit(Issue{issue.currency, account}); + STAmount final_balance = amount; + + final_balance.setIssuer(noAccount()); + + auto const receiverAccount = view.peek(keylet::account(account)); + if (!receiverAccount) + return tefINTERNAL; // LCOV_EXCL_LINE + + bool const noRipple = (receiverAccount->getFlags() & lsfDefaultRipple) == 0; + + return trustCreate( + view, + bSenderHigh, + issue.account, + account, + index.key, + receiverAccount, + false, + noRipple, + false, + false, + final_balance, + limit, + 0, + 0, + j); +} + +TER +redeemIOU( + ApplyView& view, + AccountID const& account, + STAmount const& amount, + Issue const& issue, + beast::Journal j) +{ + XRPL_ASSERT( + !isXRP(account) && !isXRP(issue.account), + "xrpl::redeemIOU : neither account nor issuer is XRP"); + + // Consistency check + XRPL_ASSERT(issue == amount.issue(), "xrpl::redeemIOU : matching issue"); + + // Can't send to self! + XRPL_ASSERT(issue.account != account, "xrpl::redeemIOU : not issuer account"); + + JLOG(j.trace()) << "redeemIOU: " << to_string(account) << ": " << amount.getFullText(); + + bool const bSenderHigh = account > issue.account; + + if (auto state = view.peek(keylet::line(account, issue.account, issue.currency))) + { + STAmount final_balance = state->getFieldAmount(sfBalance); + + if (bSenderHigh) + final_balance.negate(); // Put balance in sender terms. + + STAmount const start_balance = final_balance; + + final_balance -= amount; + + auto const must_delete = + updateTrustLine(view, state, bSenderHigh, account, start_balance, final_balance, j); + + view.creditHook(account, issue.account, amount, start_balance); + + if (bSenderHigh) + final_balance.negate(); + + // Adjust the balance on the trust line if necessary. We do this even + // if we are going to delete the line to reflect the correct balance + // at the time of deletion. + state->setFieldAmount(sfBalance, final_balance); + + if (must_delete) + { + return trustDelete( + view, + state, + bSenderHigh ? issue.account : account, + bSenderHigh ? account : issue.account, + j); + } + + view.update(state); + return tesSUCCESS; + } + + // In order to hold an IOU, a trust line *MUST* exist to track the + // balance. If it doesn't, then something is very wrong. Don't try + // to continue. + // LCOV_EXCL_START + JLOG(j.fatal()) << "redeemIOU: " << to_string(account) << " attempts to " + << "redeem " << amount.getFullText() << " but no trust line exists!"; + + return tefINTERNAL; + // LCOV_EXCL_STOP +} + +//------------------------------------------------------------------------------ +// +// Authorization and transfer checks (IOU-specific) +// +//------------------------------------------------------------------------------ + +TER +requireAuth(ReadView const& view, Issue const& issue, AccountID const& account, AuthType authType) +{ + if (isXRP(issue) || issue.account == account) + return tesSUCCESS; + + auto const trustLine = view.read(keylet::line(account, issue.account, issue.currency)); + // If account has no line, and this is a strong check, fail + if (!trustLine && authType == AuthType::StrongAuth) + return tecNO_LINE; + + // If this is a weak or legacy check, or if the account has a line, fail if + // auth is required and not set on the line + if (auto const issuerAccount = view.read(keylet::account(issue.account)); + issuerAccount && (((*issuerAccount)[sfFlags] & lsfRequireAuth) != 0u)) + { + if (trustLine) + { + return (((*trustLine)[sfFlags] & + ((account > issue.account) ? lsfLowAuth : lsfHighAuth)) != 0u) + ? tesSUCCESS + : TER{tecNO_AUTH}; + } + return TER{tecNO_LINE}; + } + + return tesSUCCESS; +} + +TER +canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, AccountID const& to) +{ + if (issue.native()) + return tesSUCCESS; + + auto const& issuerId = issue.getIssuer(); + if (issuerId == from || issuerId == to) + return tesSUCCESS; + auto const sleIssuer = view.read(keylet::account(issuerId)); + if (sleIssuer == nullptr) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const isRippleDisabled = [&](AccountID account) -> bool { + // Line might not exist, but some transfers can create it. If this + // is the case, just check the default ripple on the issuer account. + auto const line = view.read(keylet::line(account, issue)); + if (line) + { + bool const issuerHigh = issuerId > account; + return line->isFlag(issuerHigh ? lsfHighNoRipple : lsfLowNoRipple); + } + return !sleIssuer->isFlag(lsfDefaultRipple); + }; + + // Fail if rippling disabled on both trust lines + if (isRippleDisabled(from) && isRippleDisabled(to)) + return terNO_RIPPLE; + + return tesSUCCESS; +} + +//------------------------------------------------------------------------------ +// +// Empty holding operations (IOU-specific) +// +//------------------------------------------------------------------------------ + +TER +addEmptyHolding( + ApplyView& view, + AccountID const& accountID, + XRPAmount priorBalance, + Issue const& issue, + beast::Journal journal) +{ + // Every account can hold XRP. An issuer can issue directly. + if (issue.native() || accountID == issue.getIssuer()) + return tesSUCCESS; + + auto const& issuerId = issue.getIssuer(); + auto const& currency = issue.currency; + if (isGlobalFrozen(view, issuerId)) + return tecFROZEN; // LCOV_EXCL_LINE + + auto const& srcId = issuerId; + auto const& dstId = accountID; + auto const high = srcId > dstId; + auto const index = keylet::line(srcId, dstId, currency); + auto const sleSrc = view.peek(keylet::account(srcId)); + auto const sleDst = view.peek(keylet::account(dstId)); + if (!sleDst || !sleSrc) + return tefINTERNAL; // LCOV_EXCL_LINE + if (!sleSrc->isFlag(lsfDefaultRipple)) + return tecINTERNAL; // LCOV_EXCL_LINE + // If the line already exists, don't create it again. + if (view.read(index)) + return tecDUPLICATE; + + // Can the account cover the trust line reserve ? + std::uint32_t const ownerCount = sleDst->at(sfOwnerCount); + if (priorBalance < view.fees().accountReserve(ownerCount + 1)) + return tecNO_LINE_INSUF_RESERVE; + + return trustCreate( + view, + high, + srcId, + dstId, + index.key, + sleDst, + /*bAuth=*/false, + /*bNoRipple=*/true, + /*bFreeze=*/false, + /*deepFreeze*/ false, + /*saBalance=*/STAmount{Issue{currency, noAccount()}}, + /*saLimit=*/STAmount{Issue{currency, dstId}}, + /*uQualityIn=*/0, + /*uQualityOut=*/0, + journal); +} + +TER +removeEmptyHolding( + ApplyView& view, + AccountID const& accountID, + Issue const& issue, + beast::Journal journal) +{ + if (issue.native()) + { + auto const sle = view.read(keylet::account(accountID)); + if (!sle) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const balance = sle->getFieldAmount(sfBalance); + if (balance.xrp() != 0) + return tecHAS_OBLIGATIONS; + + return tesSUCCESS; + } + + // `asset` is an IOU. + // If the account is the issuer, then no line should exist. Check anyway. + // If a line does exist, it will get deleted. If not, return success. + bool const accountIsIssuer = accountID == issue.account; + auto const line = view.peek(keylet::line(accountID, issue)); + if (!line) + return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND; + if (!accountIsIssuer && line->at(sfBalance)->iou() != beast::zero) + return tecHAS_OBLIGATIONS; + + // Adjust the owner count(s) + if (line->isFlag(lsfLowReserve)) + { + // Clear reserve for low account. + auto sleLowAccount = view.peek(keylet::account(line->at(sfLowLimit)->getIssuer())); + if (!sleLowAccount) + return tecINTERNAL; // LCOV_EXCL_LINE + + adjustOwnerCount(view, sleLowAccount, -1, journal); + // It's not really necessary to clear the reserve flag, since the line + // is about to be deleted, but this will make the metadata reflect an + // accurate state at the time of deletion. + line->clearFlag(lsfLowReserve); + } + + if (line->isFlag(lsfHighReserve)) + { + // Clear reserve for high account. + auto sleHighAccount = view.peek(keylet::account(line->at(sfHighLimit)->getIssuer())); + if (!sleHighAccount) + return tecINTERNAL; // LCOV_EXCL_LINE + + adjustOwnerCount(view, sleHighAccount, -1, journal); + // It's not really necessary to clear the reserve flag, since the line + // is about to be deleted, but this will make the metadata reflect an + // accurate state at the time of deletion. + line->clearFlag(lsfHighReserve); + } + + return trustDelete( + view, line, line->at(sfLowLimit)->getIssuer(), line->at(sfHighLimit)->getIssuer(), journal); +} + +TER +deleteAMMTrustLine( + ApplyView& view, + std::shared_ptr sleState, + std::optional const& ammAccountID, + beast::Journal j) +{ + if (!sleState || sleState->getType() != ltRIPPLE_STATE) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const& [low, high] = std::minmax( + sleState->getFieldAmount(sfLowLimit).getIssuer(), + sleState->getFieldAmount(sfHighLimit).getIssuer()); + auto sleLow = view.peek(keylet::account(low)); + auto sleHigh = view.peek(keylet::account(high)); + if (!sleLow || !sleHigh) + return tecINTERNAL; // LCOV_EXCL_LINE + + bool const ammLow = sleLow->isFieldPresent(sfAMMID); + bool const ammHigh = sleHigh->isFieldPresent(sfAMMID); + + // can't both be AMM + if (ammLow && ammHigh) + return tecINTERNAL; // LCOV_EXCL_LINE + + // at least one must be + if (!ammLow && !ammHigh) + return terNO_AMM; + + // one must be the target amm + if (ammAccountID && (low != *ammAccountID && high != *ammAccountID)) + return terNO_AMM; + + if (auto const ter = trustDelete(view, sleState, low, high, j); !isTesSuccess(ter)) + { + JLOG(j.error()) << "deleteAMMTrustLine: failed to delete the trustline."; + return ter; + } + + auto const uFlags = !ammLow ? lsfLowReserve : lsfHighReserve; + if ((sleState->getFlags() & uFlags) == 0u) + return tecINTERNAL; // LCOV_EXCL_LINE + + adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/TokenHelpers.cpp b/src/libxrpl/ledger/helpers/TokenHelpers.cpp new file mode 100644 index 0000000000..332cfd83b2 --- /dev/null +++ b/src/libxrpl/ledger/helpers/TokenHelpers.cpp @@ -0,0 +1,1424 @@ +#include +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +// Forward declaration for function that remains in View.h/cpp +bool +isLPTokenFrozen( + ReadView const& view, + AccountID const& account, + Issue const& asset, + Issue const& asset2); + +//------------------------------------------------------------------------------ +// +// Freeze checking (Asset-based) +// +//------------------------------------------------------------------------------ + +bool +isGlobalFrozen(ReadView const& view, Asset const& asset) +{ + return std::visit( + [&](TIss const& issue) { + if constexpr (std::is_same_v) + { + return isGlobalFrozen(view, issue.getIssuer()); + } + else + { + return isGlobalFrozen(view, issue); + } + }, + asset.value()); +} + +bool +isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset) +{ + return std::visit( + [&](auto const& issue) { return isIndividualFrozen(view, account, issue); }, asset.value()); +} + +bool +isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth) +{ + return std::visit( + [&](auto const& issue) { return isFrozen(view, account, issue, depth); }, asset.value()); +} + +TER +checkFrozen(ReadView const& view, AccountID const& account, Issue const& issue) +{ + return isFrozen(view, account, issue) ? (TER)tecFROZEN : (TER)tesSUCCESS; +} + +TER +checkFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue) +{ + return isFrozen(view, account, mptIssue) ? (TER)tecLOCKED : (TER)tesSUCCESS; +} + +TER +checkFrozen(ReadView const& view, AccountID const& account, Asset const& asset) +{ + return std::visit( + [&](auto const& issue) { return checkFrozen(view, account, issue); }, asset.value()); +} + +bool +isAnyFrozen( + ReadView const& view, + std::initializer_list const& accounts, + Issue const& issue) +{ + for (auto const& account : accounts) + { + if (isFrozen(view, account, issue.currency, issue.account)) + return true; + } + return false; +} + +bool +isAnyFrozen( + ReadView const& view, + std::initializer_list const& accounts, + Asset const& asset, + int depth) +{ + return std::visit( + [&](TIss const& issue) { + if constexpr (std::is_same_v) + { + return isAnyFrozen(view, accounts, issue); + } + else + { + return isAnyFrozen(view, accounts, issue, depth); + } + }, + asset.value()); +} + +bool +isDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth) +{ + // Unlike IOUs, frozen / locked MPTs are not allowed to send or receive + // funds, so checking "deep frozen" is the same as checking "frozen". + return isFrozen(view, account, mptIssue, depth); +} + +bool +isDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth) +{ + return std::visit( + [&](auto const& issue) { return isDeepFrozen(view, account, issue, depth); }, + asset.value()); +} + +TER +checkDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue) +{ + return isDeepFrozen(view, account, mptIssue) ? (TER)tecLOCKED : (TER)tesSUCCESS; +} + +TER +checkDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset) +{ + return std::visit( + [&](auto const& issue) { return checkDeepFrozen(view, account, issue); }, asset.value()); +} + +//------------------------------------------------------------------------------ +// +// Account balance functions +// +//------------------------------------------------------------------------------ + +static SLE::const_pointer +getLineIfUsable( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer, + FreezeHandling zeroIfFrozen, + beast::Journal j) +{ + auto sle = view.read(keylet::line(account, issuer, currency)); + + if (!sle) + { + return nullptr; + } + + if (zeroIfFrozen == fhZERO_IF_FROZEN) + { + if (isFrozen(view, account, currency, issuer) || + isDeepFrozen(view, account, currency, issuer)) + { + return nullptr; + } + + // when fixFrozenLPTokenTransfer is enabled, if currency is lptoken, + // we need to check if the associated assets have been frozen + if (view.rules().enabled(fixFrozenLPTokenTransfer)) + { + auto const sleIssuer = view.read(keylet::account(issuer)); + if (!sleIssuer) + { + return nullptr; // LCOV_EXCL_LINE + } + if (sleIssuer->isFieldPresent(sfAMMID)) + { + auto const sleAmm = view.read(keylet::amm((*sleIssuer)[sfAMMID])); + + if (!sleAmm || + isLPTokenFrozen( + view, + account, + (*sleAmm)[sfAsset].get(), + (*sleAmm)[sfAsset2].get())) + { + return nullptr; + } + } + } + } + + return sle; +} + +static STAmount +getTrustLineBalance( + ReadView const& view, + SLE::const_ref sle, + AccountID const& account, + Currency const& currency, + AccountID const& issuer, + bool includeOppositeLimit, + beast::Journal j) +{ + STAmount amount; + if (sle) + { + amount = sle->getFieldAmount(sfBalance); + bool const accountHigh = account > issuer; + auto const& oppositeField = accountHigh ? sfLowLimit : sfHighLimit; + if (accountHigh) + { + // Put balance in account terms. + amount.negate(); + } + if (includeOppositeLimit) + { + amount += sle->getFieldAmount(oppositeField); + } + amount.setIssuer(issuer); + } + else + { + amount.clear(Issue{currency, issuer}); + } + + JLOG(j.trace()) << "getTrustLineBalance:" << " account=" << to_string(account) + << " amount=" << amount.getFullText(); + + return view.balanceHook(account, issuer, amount); +} + +STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer, + FreezeHandling zeroIfFrozen, + beast::Journal j, + SpendableHandling includeFullBalance) +{ + STAmount const amount; + if (isXRP(currency)) + { + return {xrpLiquid(view, account, 0, j)}; + } + + bool const returnSpendable = (includeFullBalance == shFULL_BALANCE); + if (returnSpendable && account == issuer) + { + // If the account is the issuer, then their limit is effectively + // infinite + return STAmount{Issue{currency, issuer}, STAmount::cMaxValue, STAmount::cMaxOffset}; + } + + // IOU: Return balance on trust line modulo freeze + SLE::const_pointer const sle = + getLineIfUsable(view, account, currency, issuer, zeroIfFrozen, j); + + return getTrustLineBalance(view, sle, account, currency, issuer, returnSpendable, j); +} + +STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + Issue const& issue, + FreezeHandling zeroIfFrozen, + beast::Journal j, + SpendableHandling includeFullBalance) +{ + return accountHolds( + view, account, issue.currency, issue.account, zeroIfFrozen, j, includeFullBalance); +} + +STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + MPTIssue const& mptIssue, + FreezeHandling zeroIfFrozen, + AuthHandling zeroIfUnauthorized, + beast::Journal j, + SpendableHandling includeFullBalance) +{ + bool const returnSpendable = (includeFullBalance == shFULL_BALANCE); + + if (returnSpendable && account == mptIssue.getIssuer()) + { + // if the account is the issuer, and the issuance exists, their limit is + // the issuance limit minus the outstanding value + auto const issuance = view.read(keylet::mptIssuance(mptIssue.getMptID())); + + if (!issuance) + { + return STAmount{mptIssue}; + } + return STAmount{ + mptIssue, + issuance->at(~sfMaximumAmount).value_or(maxMPTokenAmount) - + issuance->at(sfOutstandingAmount)}; + } + + STAmount amount; + + auto const sleMpt = view.read(keylet::mptoken(mptIssue.getMptID(), account)); + + if (!sleMpt) + { + amount.clear(mptIssue); + } + else if (zeroIfFrozen == fhZERO_IF_FROZEN && isFrozen(view, account, mptIssue)) + { + amount.clear(mptIssue); + } + else + { + amount = STAmount{mptIssue, sleMpt->getFieldU64(sfMPTAmount)}; + + // Only if auth check is needed, as it needs to do an additional read + // operation. Note featureSingleAssetVault will affect error codes. + if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED && + view.rules().enabled(featureSingleAssetVault)) + { + if (auto const err = requireAuth(view, mptIssue, account, AuthType::StrongAuth); + !isTesSuccess(err)) + amount.clear(mptIssue); + } + else if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED) + { + auto const sleIssuance = view.read(keylet::mptIssuance(mptIssue.getMptID())); + + // if auth is enabled on the issuance and mpt is not authorized, + // clear amount + if (sleIssuance && sleIssuance->isFlag(lsfMPTRequireAuth) && + !sleMpt->isFlag(lsfMPTAuthorized)) + amount.clear(mptIssue); + } + } + + return amount; +} + +[[nodiscard]] STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + Asset const& asset, + FreezeHandling zeroIfFrozen, + AuthHandling zeroIfUnauthorized, + beast::Journal j, + SpendableHandling includeFullBalance) +{ + return std::visit( + [&](TIss const& value) { + if constexpr (std::is_same_v) + { + return accountHolds(view, account, value, zeroIfFrozen, j, includeFullBalance); + } + else if constexpr (std::is_same_v) + { + return accountHolds( + view, account, value, zeroIfFrozen, zeroIfUnauthorized, j, includeFullBalance); + } + }, + asset.value()); +} + +STAmount +accountFunds( + ReadView const& view, + AccountID const& id, + STAmount const& saDefault, + FreezeHandling freezeHandling, + beast::Journal j) +{ + if (!saDefault.native() && saDefault.getIssuer() == id) + return saDefault; + + return accountHolds( + view, id, saDefault.getCurrency(), saDefault.getIssuer(), freezeHandling, j); +} + +Rate +transferRate(ReadView const& view, STAmount const& amount) +{ + return std::visit( + [&](TIss const& issue) { + if constexpr (std::is_same_v) + { + return transferRate(view, issue.getIssuer()); + } + else + { + return transferRate(view, issue.getMptID()); + } + }, + amount.asset().value()); +} + +//------------------------------------------------------------------------------ +// +// Holding operations +// +//------------------------------------------------------------------------------ + +[[nodiscard]] TER +canAddHolding(ReadView const& view, Issue const& issue) +{ + if (issue.native()) + { + return tesSUCCESS; // No special checks for XRP + } + + auto const issuer = view.read(keylet::account(issue.getIssuer())); + if (!issuer) + { + return terNO_ACCOUNT; + } + if (!issuer->isFlag(lsfDefaultRipple)) + { + return terNO_RIPPLE; + } + + return tesSUCCESS; +} + +[[nodiscard]] TER +canAddHolding(ReadView const& view, Asset const& asset) +{ + return std::visit( + [&](TIss const& issue) -> TER { return canAddHolding(view, issue); }, + asset.value()); +} + +TER +addEmptyHolding( + ApplyView& view, + AccountID const& accountID, + XRPAmount priorBalance, + Asset const& asset, + beast::Journal journal) +{ + return std::visit( + [&](TIss const& issue) -> TER { + return addEmptyHolding(view, accountID, priorBalance, issue, journal); + }, + asset.value()); +} + +TER +removeEmptyHolding( + ApplyView& view, + AccountID const& accountID, + Asset const& asset, + beast::Journal journal) +{ + return std::visit( + [&](TIss const& issue) -> TER { + return removeEmptyHolding(view, accountID, issue, journal); + }, + asset.value()); +} + +//------------------------------------------------------------------------------ +// +// Authorization and transfer checks +// +//------------------------------------------------------------------------------ + +TER +requireAuth(ReadView const& view, Asset const& asset, AccountID const& account, AuthType authType) +{ + return std::visit( + [&](TIss const& issue_) { + return requireAuth(view, issue_, account, authType); + }, + asset.value()); +} + +TER +canTransfer(ReadView const& view, Asset const& asset, AccountID const& from, AccountID const& to) +{ + return std::visit( + [&](TIss const& issue) -> TER { + return canTransfer(view, issue, from, to); + }, + asset.value()); +} + +//------------------------------------------------------------------------------ +// +// Money Transfers +// +//------------------------------------------------------------------------------ + +// Direct send w/o fees: +// - Redeeming IOUs and/or sending sender's own IOUs. +// - Create trust line if needed. +// --> bCheckIssuer : normally require issuer to be involved. +static TER +directSendNoFeeIOU( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + bool bCheckIssuer, + beast::Journal j) +{ + AccountID const& issuer = saAmount.getIssuer(); + Currency const& currency = saAmount.getCurrency(); + + // Make sure issuer is involved. + XRPL_ASSERT( + !bCheckIssuer || uSenderID == issuer || uReceiverID == issuer, + "xrpl::directSendNoFeeIOU : matching issuer or don't care"); + (void)issuer; + + // Disallow sending to self. + XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::directSendNoFeeIOU : sender is not receiver"); + + bool const bSenderHigh = uSenderID > uReceiverID; + auto const index = keylet::line(uSenderID, uReceiverID, currency); + + XRPL_ASSERT( + !isXRP(uSenderID) && uSenderID != noAccount(), + "xrpl::directSendNoFeeIOU : sender is not XRP"); + XRPL_ASSERT( + !isXRP(uReceiverID) && uReceiverID != noAccount(), + "xrpl::directSendNoFeeIOU : receiver is not XRP"); + + // If the line exists, modify it accordingly. + if (auto const sleRippleState = view.peek(index)) + { + STAmount saBalance = sleRippleState->getFieldAmount(sfBalance); + + if (bSenderHigh) + saBalance.negate(); // Put balance in sender terms. + + view.creditHook(uSenderID, uReceiverID, saAmount, saBalance); + + STAmount const saBefore = saBalance; + + saBalance -= saAmount; + + JLOG(j.trace()) << "directSendNoFeeIOU: " << to_string(uSenderID) << " -> " + << to_string(uReceiverID) << " : before=" << saBefore.getFullText() + << " amount=" << saAmount.getFullText() + << " after=" << saBalance.getFullText(); + + std::uint32_t const uFlags(sleRippleState->getFieldU32(sfFlags)); + bool bDelete = false; + + // FIXME This NEEDS to be cleaned up and simplified. It's impossible + // for anyone to understand. + if (saBefore > beast::zero + // Sender balance was positive. + && saBalance <= beast::zero + // Sender is zero or negative. + && ((uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) != 0u) + // Sender reserve is set. + && static_cast(uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != + static_cast( + view.read(keylet::account(uSenderID))->getFlags() & lsfDefaultRipple) && + ((uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) == 0u) && + !sleRippleState->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit) + // Sender trust limit is 0. + && (sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) == 0u) + // Sender quality in is 0. + && + (sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut) == 0u)) + // Sender quality out is 0. + { + // Clear the reserve of the sender, possibly delete the line! + adjustOwnerCount(view, view.peek(keylet::account(uSenderID)), -1, j); + + // Clear reserve flag. + sleRippleState->setFieldU32( + sfFlags, uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); + + // Balance is zero, receiver reserve is clear. + bDelete = !saBalance // Balance is zero. + && ((uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)) == 0u); + // Receiver reserve is clear. + } + + if (bSenderHigh) + saBalance.negate(); + + // Want to reflect balance to zero even if we are deleting line. + sleRippleState->setFieldAmount(sfBalance, saBalance); + // ONLY: Adjust balance. + + if (bDelete) + { + return trustDelete( + view, + sleRippleState, + bSenderHigh ? uReceiverID : uSenderID, + !bSenderHigh ? uReceiverID : uSenderID, + j); + } + + view.update(sleRippleState); + return tesSUCCESS; + } + + STAmount const saReceiverLimit(Issue{currency, uReceiverID}); + STAmount saBalance{saAmount}; + + saBalance.setIssuer(noAccount()); + + JLOG(j.debug()) << "directSendNoFeeIOU: " + "create line: " + << to_string(uSenderID) << " -> " << to_string(uReceiverID) << " : " + << saAmount.getFullText(); + + auto const sleAccount = view.peek(keylet::account(uReceiverID)); + if (!sleAccount) + return tefINTERNAL; // LCOV_EXCL_LINE + + bool const noRipple = (sleAccount->getFlags() & lsfDefaultRipple) == 0; + + return trustCreate( + view, + bSenderHigh, + uSenderID, + uReceiverID, + index.key, + sleAccount, + false, + noRipple, + false, + false, + saBalance, + saReceiverLimit, + 0, + 0, + j); +} + +// Send regardless of limits. +// --> saAmount: Amount/currency/issuer to deliver to receiver. +// <-- saActual: Amount actually cost. Sender pays fees. +static TER +directSendNoLimitIOU( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + STAmount& saActual, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + auto const& issuer = saAmount.getIssuer(); + + XRPL_ASSERT( + !isXRP(uSenderID) && !isXRP(uReceiverID), + "xrpl::directSendNoLimitIOU : neither sender nor receiver is XRP"); + XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::directSendNoLimitIOU : sender is not receiver"); + + if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount()) + { + // Direct send: redeeming IOUs and/or sending own IOUs. + auto const ter = directSendNoFeeIOU(view, uSenderID, uReceiverID, saAmount, false, j); + if (!isTesSuccess(ter)) + return ter; + saActual = saAmount; + return tesSUCCESS; + } + + // Sending 3rd party IOUs: transit. + + // Calculate the amount to transfer accounting + // for any transfer fees if the fee is not waived: + saActual = (waiveFee == WaiveTransferFee::Yes) ? saAmount + : multiply(saAmount, transferRate(view, issuer)); + + JLOG(j.debug()) << "directSendNoLimitIOU> " << to_string(uSenderID) << " - > " + << to_string(uReceiverID) << " : deliver=" << saAmount.getFullText() + << " cost=" << saActual.getFullText(); + + TER terResult = directSendNoFeeIOU(view, issuer, uReceiverID, saAmount, true, j); + + if (tesSUCCESS == terResult) + terResult = directSendNoFeeIOU(view, uSenderID, issuer, saActual, true, j); + + return terResult; +} + +// Send regardless of limits. +// --> receivers: Amount/currency/issuer to deliver to receivers. +// <-- saActual: Amount actually cost to sender. Sender pays fees. +static TER +directSendNoLimitMultiIOU( + ApplyView& view, + AccountID const& senderID, + Issue const& issue, + MultiplePaymentDestinations const& receivers, + STAmount& actual, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + auto const& issuer = issue.getIssuer(); + + XRPL_ASSERT(!isXRP(senderID), "xrpl::directSendNoLimitMultiIOU : sender is not XRP"); + + // These may diverge + STAmount takeFromSender{issue}; + actual = takeFromSender; + + // Failures return immediately. + for (auto const& r : receivers) + { + auto const& receiverID = r.first; + STAmount const amount{issue, r.second}; + + /* If we aren't sending anything or if the sender is the same as the + * receiver then we don't need to do anything. + */ + if (!amount || (senderID == receiverID)) + continue; + + XRPL_ASSERT(!isXRP(receiverID), "xrpl::directSendNoLimitMultiIOU : receiver is not XRP"); + + if (senderID == issuer || receiverID == issuer || issuer == noAccount()) + { + // Direct send: redeeming IOUs and/or sending own IOUs. + if (auto const ter = directSendNoFeeIOU(view, senderID, receiverID, amount, false, j)) + return ter; + actual += amount; + // Do not add amount to takeFromSender, because directSendNoFeeIOU took + // it. + + continue; + } + + // Sending 3rd party IOUs: transit. + + // Calculate the amount to transfer accounting + // for any transfer fees if the fee is not waived: + STAmount const actualSend = (waiveFee == WaiveTransferFee::Yes) + ? amount + : multiply(amount, transferRate(view, issuer)); + actual += actualSend; + takeFromSender += actualSend; + + JLOG(j.debug()) << "directSendNoLimitMultiIOU> " << to_string(senderID) << " - > " + << to_string(receiverID) << " : deliver=" << amount.getFullText() + << " cost=" << actual.getFullText(); + + if (TER const terResult = directSendNoFeeIOU(view, issuer, receiverID, amount, true, j)) + return terResult; + } + + if (senderID != issuer && takeFromSender) + { + if (TER const terResult = + directSendNoFeeIOU(view, senderID, issuer, takeFromSender, true, j)) + return terResult; + } + + return tesSUCCESS; +} + +static TER +accountSendIOU( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + if (view.rules().enabled(fixAMMv1_1)) + { + if (saAmount < beast::zero || saAmount.holds()) + { + return tecINTERNAL; // LCOV_EXCL_LINE + } + } + else + { + // LCOV_EXCL_START + XRPL_ASSERT( + saAmount >= beast::zero && !saAmount.holds(), + "xrpl::accountSendIOU : minimum amount and not MPT"); + // LCOV_EXCL_STOP + } + + /* If we aren't sending anything or if the sender is the same as the + * receiver then we don't need to do anything. + */ + if (!saAmount || (uSenderID == uReceiverID)) + return tesSUCCESS; + + if (!saAmount.native()) + { + STAmount saActual; + + JLOG(j.trace()) << "accountSendIOU: " << to_string(uSenderID) << " -> " + << to_string(uReceiverID) << " : " << saAmount.getFullText(); + + return directSendNoLimitIOU(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee); + } + + /* XRP send which does not check reserve and can do pure adjustment. + * Note that sender or receiver may be null and this not a mistake; this + * setup is used during pathfinding and it is carefully controlled to + * ensure that transfers are balanced. + */ + TER terResult(tesSUCCESS); + + SLE::pointer const sender = + uSenderID != beast::zero ? view.peek(keylet::account(uSenderID)) : SLE::pointer(); + SLE::pointer const receiver = + uReceiverID != beast::zero ? view.peek(keylet::account(uReceiverID)) : SLE::pointer(); + + if (auto stream = j.trace()) + { + std::string sender_bal("-"); + std::string receiver_bal("-"); + + if (sender) + sender_bal = sender->getFieldAmount(sfBalance).getFullText(); + + if (receiver) + receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); + + stream << "accountSendIOU> " << to_string(uSenderID) << " (" << sender_bal << ") -> " + << to_string(uReceiverID) << " (" << receiver_bal + << ") : " << saAmount.getFullText(); + } + + if (sender) + { + if (sender->getFieldAmount(sfBalance) < saAmount) + { + // VFALCO Its laborious to have to mutate the + // TER based on params everywhere + // LCOV_EXCL_START + terResult = view.open() ? TER{telFAILED_PROCESSING} : TER{tecFAILED_PROCESSING}; + // LCOV_EXCL_STOP + } + else + { + auto const sndBal = sender->getFieldAmount(sfBalance); + view.creditHook(uSenderID, xrpAccount(), saAmount, sndBal); + + // Decrement XRP balance. + sender->setFieldAmount(sfBalance, sndBal - saAmount); + view.update(sender); + } + } + + if (tesSUCCESS == terResult && receiver) + { + // Increment XRP balance. + auto const rcvBal = receiver->getFieldAmount(sfBalance); + receiver->setFieldAmount(sfBalance, rcvBal + saAmount); + view.creditHook(xrpAccount(), uReceiverID, saAmount, -rcvBal); + + view.update(receiver); + } + + if (auto stream = j.trace()) + { + std::string sender_bal("-"); + std::string receiver_bal("-"); + + if (sender) + sender_bal = sender->getFieldAmount(sfBalance).getFullText(); + + if (receiver) + receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); + + stream << "accountSendIOU< " << to_string(uSenderID) << " (" << sender_bal << ") -> " + << to_string(uReceiverID) << " (" << receiver_bal + << ") : " << saAmount.getFullText(); + } + + return terResult; +} + +static TER +accountSendMultiIOU( + ApplyView& view, + AccountID const& senderID, + Issue const& issue, + MultiplePaymentDestinations const& receivers, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + XRPL_ASSERT_PARTS( + receivers.size() > 1, "xrpl::accountSendMultiIOU", "multiple recipients provided"); + + if (!issue.native()) + { + STAmount actual; + JLOG(j.trace()) << "accountSendMultiIOU: " << to_string(senderID) << " sending " + << receivers.size() << " IOUs"; + + return directSendNoLimitMultiIOU(view, senderID, issue, receivers, actual, j, waiveFee); + } + + /* XRP send which does not check reserve and can do pure adjustment. + * Note that sender or receiver may be null and this not a mistake; this + * setup could be used during pathfinding and it is carefully controlled to + * ensure that transfers are balanced. + */ + + SLE::pointer const sender = + senderID != beast::zero ? view.peek(keylet::account(senderID)) : SLE::pointer(); + + if (auto stream = j.trace()) + { + std::string sender_bal("-"); + + if (sender) + sender_bal = sender->getFieldAmount(sfBalance).getFullText(); + + stream << "accountSendMultiIOU> " << to_string(senderID) << " (" << sender_bal << ") -> " + << receivers.size() << " receivers."; + } + + // Failures return immediately. + STAmount takeFromSender{issue}; + for (auto const& r : receivers) + { + auto const& receiverID = r.first; + STAmount const amount{issue, r.second}; + + if (amount < beast::zero) + { + return tecINTERNAL; // LCOV_EXCL_LINE + } + + /* If we aren't sending anything or if the sender is the same as the + * receiver then we don't need to do anything. + */ + if (!amount || (senderID == receiverID)) + continue; + + SLE::pointer const receiver = + receiverID != beast::zero ? view.peek(keylet::account(receiverID)) : SLE::pointer(); + + if (auto stream = j.trace()) + { + std::string receiver_bal("-"); + + if (receiver) + receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); + + stream << "accountSendMultiIOU> " << to_string(senderID) << " -> " + << to_string(receiverID) << " (" << receiver_bal + << ") : " << amount.getFullText(); + } + + if (receiver) + { + // Increment XRP balance. + auto const rcvBal = receiver->getFieldAmount(sfBalance); + receiver->setFieldAmount(sfBalance, rcvBal + amount); + view.creditHook(xrpAccount(), receiverID, amount, -rcvBal); + + view.update(receiver); + + // Take what is actually sent + takeFromSender += amount; + } + + if (auto stream = j.trace()) + { + std::string receiver_bal("-"); + + if (receiver) + receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); + + stream << "accountSendMultiIOU< " << to_string(senderID) << " -> " + << to_string(receiverID) << " (" << receiver_bal + << ") : " << amount.getFullText(); + } + } + + if (sender) + { + if (sender->getFieldAmount(sfBalance) < takeFromSender) + { + return TER{tecFAILED_PROCESSING}; + } + auto const sndBal = sender->getFieldAmount(sfBalance); + view.creditHook(senderID, xrpAccount(), takeFromSender, sndBal); + + // Decrement XRP balance. + sender->setFieldAmount(sfBalance, sndBal - takeFromSender); + view.update(sender); + } + + if (auto stream = j.trace()) + { + std::string sender_bal("-"); + + if (sender) + sender_bal = sender->getFieldAmount(sfBalance).getFullText(); + + stream << "accountSendMultiIOU< " << to_string(senderID) << " (" << sender_bal << ") -> " + << receivers.size() << " receivers."; + } + return tesSUCCESS; +} + +static TER +directSendNoFeeMPT( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + beast::Journal j) +{ + // Do not check MPT authorization here - it must have been checked earlier + auto const mptID = keylet::mptIssuance(saAmount.get().getMptID()); + auto const& issuer = saAmount.getIssuer(); + auto sleIssuance = view.peek(mptID); + if (!sleIssuance) + return tecOBJECT_NOT_FOUND; + if (uSenderID == issuer) + { + (*sleIssuance)[sfOutstandingAmount] += saAmount.mpt().value(); + view.update(sleIssuance); + } + else + { + auto const mptokenID = keylet::mptoken(mptID.key, uSenderID); + if (auto sle = view.peek(mptokenID)) + { + auto const amt = sle->getFieldU64(sfMPTAmount); + auto const pay = saAmount.mpt().value(); + if (amt < pay) + return tecINSUFFICIENT_FUNDS; + (*sle)[sfMPTAmount] = amt - pay; + view.update(sle); + } + else + { + return tecNO_AUTH; + } + } + + if (uReceiverID == issuer) + { + auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount); + auto const redeem = saAmount.mpt().value(); + if (outstanding >= redeem) + { + sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem); + view.update(sleIssuance); + } + else + { + return tecINTERNAL; // LCOV_EXCL_LINE + } + } + else + { + auto const mptokenID = keylet::mptoken(mptID.key, uReceiverID); + if (auto sle = view.peek(mptokenID)) + { + (*sle)[sfMPTAmount] += saAmount.mpt().value(); + view.update(sle); + } + else + { + return tecNO_AUTH; + } + } + + return tesSUCCESS; +} + +static TER +directSendNoLimitMPT( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + STAmount& saActual, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::directSendNoLimitMPT : sender is not receiver"); + + // Safe to get MPT since directSendNoLimitMPT is only called by accountSendMPT + auto const& issuer = saAmount.getIssuer(); + + auto const sle = view.read(keylet::mptIssuance(saAmount.get().getMptID())); + if (!sle) + return tecOBJECT_NOT_FOUND; + + if (uSenderID == issuer || uReceiverID == issuer) + { + // if sender is issuer, check that the new OutstandingAmount will not + // exceed MaximumAmount + if (uSenderID == issuer) + { + auto const sendAmount = saAmount.mpt().value(); + auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount); + if (sendAmount > maximumAmount || + sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount) + return tecPATH_DRY; + } + + // Direct send: redeeming MPTs and/or sending own MPTs. + auto const ter = directSendNoFeeMPT(view, uSenderID, uReceiverID, saAmount, j); + if (!isTesSuccess(ter)) + return ter; + saActual = saAmount; + return tesSUCCESS; + } + + // Sending 3rd party MPTs: transit. + saActual = (waiveFee == WaiveTransferFee::Yes) + ? saAmount + : multiply(saAmount, transferRate(view, saAmount.get().getMptID())); + + JLOG(j.debug()) << "directSendNoLimitMPT> " << to_string(uSenderID) << " - > " + << to_string(uReceiverID) << " : deliver=" << saAmount.getFullText() + << " cost=" << saActual.getFullText(); + + if (auto const terResult = directSendNoFeeMPT(view, issuer, uReceiverID, saAmount, j); + !isTesSuccess(terResult)) + return terResult; + + return directSendNoFeeMPT(view, uSenderID, issuer, saActual, j); +} + +static TER +directSendNoLimitMultiMPT( + ApplyView& view, + AccountID const& senderID, + MPTIssue const& mptIssue, + MultiplePaymentDestinations const& receivers, + STAmount& actual, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + auto const& issuer = mptIssue.getIssuer(); + + auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID())); + if (!sle) + return tecOBJECT_NOT_FOUND; + + // For the issuer-as-sender case, track the running total to validate + // against MaximumAmount. The read-only SLE (view.read) is not updated + // by directSendNoFeeMPT, so a per-iteration SLE read would be stale. + // Use uint64_t, not STAmount, to keep MaximumAmount comparisons in exact + // integer arithmetic. STAmount implicitly converts to Number, whose + // small-scale mantissa (~16 digits) can lose precision for values near + // maxMPTokenAmount (19 digits). + std::uint64_t totalSendAmount{0}; + std::uint64_t const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount); + std::uint64_t const outstandingAmount = sle->getFieldU64(sfOutstandingAmount); + + // actual accumulates the total cost to the sender (includes transfer + // fees for third-party transit sends). takeFromSender accumulates only + // the transit portion that is debited to the issuer in bulk after the + // loop. They diverge when there are transfer fees. + STAmount takeFromSender{mptIssue}; + actual = takeFromSender; + + for (auto const& [receiverID, amt] : receivers) + { + STAmount const amount{mptIssue, amt}; + + if (amount < beast::zero) + return tecINTERNAL; // LCOV_EXCL_LINE + + if (!amount || senderID == receiverID) + continue; + + if (senderID == issuer || receiverID == issuer) + { + if (senderID == issuer) + { + XRPL_ASSERT_PARTS( + takeFromSender == beast::zero, + "xrpl::directSendNoLimitMultiMPT", + "sender == issuer, takeFromSender == zero"); + + std::uint64_t const sendAmount = amount.mpt().value(); + + if (view.rules().enabled(fixSecurity3_1_3)) + { + // Post-fixSecurity3_1_3: aggregate MaximumAmount + // check. WARNING: the order of conditions is + // critical — each guards the subtraction in the + // next against unsigned underflow. Do not reorder. + bool const exceedsMaximumAmount = + // This send alone exceeds the max cap + sendAmount > maximumAmount || + // The aggregate of all sends exceeds the max cap + totalSendAmount > maximumAmount - sendAmount || + // Outstanding + aggregate exceeds the max cap + outstandingAmount > maximumAmount - sendAmount - totalSendAmount; + + if (exceedsMaximumAmount) + return tecPATH_DRY; + totalSendAmount += sendAmount; + } + else + { + // Pre-fixSecurity3_1_3: per-iteration MaximumAmount + // check. Reads sfOutstandingAmount from a stale + // view.read() snapshot — incorrect for multi-destination + // sends but retained for ledger replay compatibility. + if (sendAmount > maximumAmount || + outstandingAmount > maximumAmount - sendAmount) + return tecPATH_DRY; + } + } + + // Direct send: redeeming MPTs and/or sending own MPTs. + if (auto const ter = directSendNoFeeMPT(view, senderID, receiverID, amount, j)) + return ter; + actual += amount; + // Do not add amount to takeFromSender, because directSendNoFeeMPT took it. + + continue; + } + + // Sending 3rd party MPTs: transit. + STAmount const actualSend = (waiveFee == WaiveTransferFee::Yes) + ? amount + : multiply(amount, transferRate(view, amount.get().getMptID())); + actual += actualSend; + takeFromSender += actualSend; + + JLOG(j.debug()) << "directSendNoLimitMultiMPT> " << to_string(senderID) << " - > " + << to_string(receiverID) << " : deliver=" << amount.getFullText() + << " cost=" << actualSend.getFullText(); + + if (auto const terResult = directSendNoFeeMPT(view, issuer, receiverID, amount, j)) + return terResult; + } + if (senderID != issuer && takeFromSender) + { + if (TER const terResult = directSendNoFeeMPT(view, senderID, issuer, takeFromSender, j)) + return terResult; + } + + return tesSUCCESS; +} + +static TER +accountSendMPT( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + XRPL_ASSERT( + saAmount >= beast::zero && saAmount.holds(), + "xrpl::accountSendMPT : minimum amount and MPT"); + + /* If we aren't sending anything or if the sender is the same as the + * receiver then we don't need to do anything. + */ + if (!saAmount || (uSenderID == uReceiverID)) + return tesSUCCESS; + + STAmount saActual{saAmount.asset()}; + + return directSendNoLimitMPT(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee); +} + +static TER +accountSendMultiMPT( + ApplyView& view, + AccountID const& senderID, + MPTIssue const& mptIssue, + MultiplePaymentDestinations const& receivers, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + STAmount actual; + + return directSendNoLimitMultiMPT(view, senderID, mptIssue, receivers, actual, j, waiveFee); +} + +//------------------------------------------------------------------------------ +// +// Public Dispatcher Functions +// +//------------------------------------------------------------------------------ + +TER +directSendNoFee( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + bool bCheckIssuer, + beast::Journal j) +{ + return std::visit( + [&](TIss const& issue) { + if constexpr (std::is_same_v) + { + return directSendNoFeeIOU(view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j); + } + else + { + XRPL_ASSERT(!bCheckIssuer, "xrpl::directSendNoFee : not checking issuer"); + return directSendNoFeeMPT(view, uSenderID, uReceiverID, saAmount, j); + } + }, + saAmount.asset().value()); +} + +TER +accountSend( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + return std::visit( + [&](TIss const& issue) { + if constexpr (std::is_same_v) + { + return accountSendIOU(view, uSenderID, uReceiverID, saAmount, j, waiveFee); + } + else + { + return accountSendMPT(view, uSenderID, uReceiverID, saAmount, j, waiveFee); + } + }, + saAmount.asset().value()); +} + +TER +accountSendMulti( + ApplyView& view, + AccountID const& senderID, + Asset const& asset, + MultiplePaymentDestinations const& receivers, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + XRPL_ASSERT_PARTS( + receivers.size() > 1, "xrpl::accountSendMulti", "multiple recipients provided"); + return std::visit( + [&](TIss const& issue) { + if constexpr (std::is_same_v) + { + return accountSendMultiIOU(view, senderID, issue, receivers, j, waiveFee); + } + else + { + return accountSendMultiMPT(view, senderID, issue, receivers, j, waiveFee); + } + }, + asset.value()); +} + +TER +transferXRP( + ApplyView& view, + AccountID const& from, + AccountID const& to, + STAmount const& amount, + beast::Journal j) +{ + XRPL_ASSERT(from != beast::zero, "xrpl::transferXRP : nonzero from account"); + XRPL_ASSERT(to != beast::zero, "xrpl::transferXRP : nonzero to account"); + XRPL_ASSERT(from != to, "xrpl::transferXRP : sender is not receiver"); + XRPL_ASSERT(amount.native(), "xrpl::transferXRP : amount is XRP"); + + SLE::pointer const sender = view.peek(keylet::account(from)); + SLE::pointer const receiver = view.peek(keylet::account(to)); + if (!sender || !receiver) + return tefINTERNAL; // LCOV_EXCL_LINE + + JLOG(j.trace()) << "transferXRP: " << to_string(from) << " -> " << to_string(to) + << ") : " << amount.getFullText(); + + if (sender->getFieldAmount(sfBalance) < amount) + { + // VFALCO Its unfortunate we have to keep + // mutating these TER everywhere + // FIXME: this logic should be moved to callers maybe? + // LCOV_EXCL_START + return view.open() ? TER{telFAILED_PROCESSING} : TER{tecFAILED_PROCESSING}; + // LCOV_EXCL_STOP + } + + // Decrement XRP balance. + sender->setFieldAmount(sfBalance, sender->getFieldAmount(sfBalance) - amount); + view.update(sender); + + receiver->setFieldAmount(sfBalance, receiver->getFieldAmount(sfBalance) + amount); + view.update(receiver); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/VaultHelpers.cpp b/src/libxrpl/ledger/helpers/VaultHelpers.cpp new file mode 100644 index 0000000000..83a1b9fc4f --- /dev/null +++ b/src/libxrpl/ledger/helpers/VaultHelpers.cpp @@ -0,0 +1,112 @@ +#include +// +#include +#include +#include + +namespace xrpl { + +[[nodiscard]] std::optional +assetsToSharesDeposit( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& assets) +{ + XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesDeposit : non-negative assets"); + XRPL_ASSERT( + assets.asset() == vault->at(sfAsset), + "xrpl::assetsToSharesDeposit : assets and vault match"); + if (assets.negative() || assets.asset() != vault->at(sfAsset)) + return std::nullopt; // LCOV_EXCL_LINE + + Number const assetTotal = vault->at(sfAssetsTotal); + STAmount shares{vault->at(sfShareMPTID)}; + if (assetTotal == 0) + { + return STAmount{ + shares.asset(), + Number(assets.mantissa(), assets.exponent() + vault->at(sfScale)).truncate()}; + } + + Number const shareTotal = issuance->at(sfOutstandingAmount); + shares = ((shareTotal * assets) / assetTotal).truncate(); + return shares; +} + +[[nodiscard]] std::optional +sharesToAssetsDeposit( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& shares) +{ + XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsDeposit : non-negative shares"); + XRPL_ASSERT( + shares.asset() == vault->at(sfShareMPTID), + "xrpl::sharesToAssetsDeposit : shares and vault match"); + if (shares.negative() || shares.asset() != vault->at(sfShareMPTID)) + return std::nullopt; // LCOV_EXCL_LINE + + Number const assetTotal = vault->at(sfAssetsTotal); + STAmount assets{vault->at(sfAsset)}; + if (assetTotal == 0) + { + return STAmount{ + assets.asset(), shares.mantissa(), shares.exponent() - vault->at(sfScale), false}; + } + + Number const shareTotal = issuance->at(sfOutstandingAmount); + assets = (assetTotal * shares) / shareTotal; + return assets; +} + +[[nodiscard]] std::optional +assetsToSharesWithdraw( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& assets, + TruncateShares truncate) +{ + XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesWithdraw : non-negative assets"); + XRPL_ASSERT( + assets.asset() == vault->at(sfAsset), + "xrpl::assetsToSharesWithdraw : assets and vault match"); + if (assets.negative() || assets.asset() != vault->at(sfAsset)) + return std::nullopt; // LCOV_EXCL_LINE + + Number assetTotal = vault->at(sfAssetsTotal); + assetTotal -= vault->at(sfLossUnrealized); + STAmount shares{vault->at(sfShareMPTID)}; + if (assetTotal == 0) + return shares; + Number const shareTotal = issuance->at(sfOutstandingAmount); + Number result = (shareTotal * assets) / assetTotal; + if (truncate == TruncateShares::yes) + result = result.truncate(); + shares = result; + return shares; +} + +[[nodiscard]] std::optional +sharesToAssetsWithdraw( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& shares) +{ + XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsWithdraw : non-negative shares"); + XRPL_ASSERT( + shares.asset() == vault->at(sfShareMPTID), + "xrpl::sharesToAssetsWithdraw : shares and vault match"); + if (shares.negative() || shares.asset() != vault->at(sfShareMPTID)) + return std::nullopt; // LCOV_EXCL_LINE + + Number assetTotal = vault->at(sfAssetsTotal); + assetTotal -= vault->at(sfLossUnrealized); + STAmount assets{vault->at(sfAsset)}; + if (assetTotal == 0) + return assets; + Number const shareTotal = issuance->at(sfOutstandingAmount); + assets = (assetTotal * shares) / shareTotal; + return assets; +} + +} // namespace xrpl diff --git a/src/libxrpl/net/HTTPClient.cpp b/src/libxrpl/net/HTTPClient.cpp index 91a64a1ca9..5cb01cb54b 100644 --- a/src/libxrpl/net/HTTPClient.cpp +++ b/src/libxrpl/net/HTTPClient.cpp @@ -26,6 +26,12 @@ HTTPClient::initializeSSLContext( httpClientSSLContext.emplace(sslVerifyDir, sslVerifyFile, sslVerify, j); } +void +HTTPClient::cleanupSSLContext() +{ + httpClientSSLContext.reset(); +} + //------------------------------------------------------------------------------ // // Fetch a web page via http or https. @@ -53,6 +59,7 @@ public: //-------------------------------------------------------------------------- void + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) makeGet(std::string const& strPath, boost::asio::streambuf& sb, std::string const& strHost) { std::ostream osRequest(&sb); @@ -340,10 +347,10 @@ public: {std::istreambuf_iterator(&mHeader)}, std::istreambuf_iterator()}; JLOG(j_.trace()) << "Header: \"" << strHeader << "\""; - static boost::regex reStatus{"\\`HTTP/1\\S+ (\\d{3}) .*\\'"}; // HTTP/1.1 200 OK - static boost::regex reSize{ + static boost::regex const reStatus{"\\`HTTP/1\\S+ (\\d{3}) .*\\'"}; // HTTP/1.1 200 OK + static boost::regex const reSize{ "\\`.*\\r\\nContent-Length:\\s+([0-9]+).*\\'", boost::regex::icase}; - static boost::regex reBody{"\\`.*\\r\\n\\r\\n(.*)\\'"}; + static boost::regex const reBody{"\\`.*\\r\\n\\r\\n(.*)\\'"}; boost::smatch smMatch; // Match status code. @@ -421,7 +428,7 @@ public: else { mResponse.commit(bytes_transferred); - std::string strBody{ + std::string const strBody{ {std::istreambuf_iterator(&mResponse)}, std::istreambuf_iterator()}; invokeComplete(ecResult, mStatus, mBody + strBody); } @@ -472,7 +479,7 @@ public: private: using pointer = std::shared_ptr; - bool mSSL; + bool mSSL{}; AutoSocket mSocket; boost::asio::ip::tcp::resolver mResolver; @@ -490,7 +497,7 @@ private: std::string mBody; unsigned short const mPort; std::size_t const maxResponseSize_; - int mStatus; + int mStatus{}; std::function mBuild; std::function< bool(boost::system::error_code const& ecResult, int iStatus, std::string const& strData)> @@ -502,7 +509,7 @@ private: boost::system::error_code mShutdown; std::deque mDeqSites; - std::chrono::seconds mTimeout; + std::chrono::seconds mTimeout{}; beast::Journal j_; }; @@ -540,7 +547,7 @@ HTTPClient::get( complete, beast::Journal& j) { - std::deque deqSites(1, strSite); + std::deque const deqSites(1, strSite); auto client = std::make_shared(io_context, port, responseMax, j); client->get(bSSL, deqSites, strPath, timeout, complete); @@ -560,7 +567,7 @@ HTTPClient::request( complete, beast::Journal& j) { - std::deque deqSites(1, strSite); + std::deque const deqSites(1, strSite); auto client = std::make_shared(io_context, port, responseMax, j); client->request(bSSL, deqSites, setRequest, timeout, complete); diff --git a/src/libxrpl/net/RegisterSSLCerts.cpp b/src/libxrpl/net/RegisterSSLCerts.cpp index 87c2eb81b7..1f21a76ddb 100644 --- a/src/libxrpl/net/RegisterSSLCerts.cpp +++ b/src/libxrpl/net/RegisterSSLCerts.cpp @@ -79,6 +79,7 @@ registerSSLCerts(boost::asio::ssl::context& ctx, boost::system::error_code& ec, SSL_CTX_set_cert_store(ctx.native_handle(), store.release()); #else + // NOLINTNEXTLINE(bugprone-unused-return-value) ctx.set_default_verify_paths(ec); #endif } diff --git a/src/libxrpl/nodestore/BatchWriter.cpp b/src/libxrpl/nodestore/BatchWriter.cpp index ed7753f68e..75b9a8dcde 100644 --- a/src/libxrpl/nodestore/BatchWriter.cpp +++ b/src/libxrpl/nodestore/BatchWriter.cpp @@ -4,7 +4,7 @@ namespace xrpl { namespace NodeStore { BatchWriter::BatchWriter(Callback& callback, Scheduler& scheduler) - : m_callback(callback), m_scheduler(scheduler), mWriteLoad(0), mWritePending(false) + : m_callback(callback), m_scheduler(scheduler) { mWriteSet.reserve(batchWritePreallocationSize); } @@ -37,7 +37,7 @@ BatchWriter::store(std::shared_ptr const& object) int BatchWriter::getWriteLoad() { - std::lock_guard sl(mWriteMutex); + std::lock_guard const sl(mWriteMutex); return std::max(mWriteLoad, static_cast(mWriteSet.size())); } @@ -58,7 +58,7 @@ BatchWriter::writeBatch() set.reserve(batchWritePreallocationSize); { - std::lock_guard sl(mWriteMutex); + std::lock_guard const sl(mWriteMutex); mWriteSet.swap(set); XRPL_ASSERT( @@ -75,7 +75,7 @@ BatchWriter::writeBatch() } } - BatchWriteReport report; + BatchWriteReport report{}; report.writeCount = set.size(); auto const before = std::chrono::steady_clock::now(); diff --git a/src/libxrpl/nodestore/Database.cpp b/src/libxrpl/nodestore/Database.cpp index 4e0d5be05d..5d9d153f57 100644 --- a/src/libxrpl/nodestore/Database.cpp +++ b/src/libxrpl/nodestore/Database.cpp @@ -122,7 +122,7 @@ void Database::stop() { { - std::lock_guard lock(readLock_); + std::lock_guard const lock(readLock_); if (!readStopping_.exchange(true, std::memory_order_relaxed)) { @@ -158,7 +158,7 @@ Database::asyncFetch( std::uint32_t ledgerSeq, std::function const&)>&& cb) { - std::lock_guard lock(readLock_); + std::lock_guard const lock(readLock_); if (!isStopping()) { @@ -238,7 +238,7 @@ Database::getCountsJson(Json::Value& obj) XRPL_ASSERT(obj.isObject(), "xrpl::NodeStore::Database::getCountsJson : valid input type"); { - std::unique_lock lock(readLock_); + std::unique_lock const lock(readLock_); obj["read_queue"] = static_cast(read_.size()); } diff --git a/src/libxrpl/nodestore/DatabaseNodeImp.cpp b/src/libxrpl/nodestore/DatabaseNodeImp.cpp index d1452dba86..a24379aea9 100644 --- a/src/libxrpl/nodestore/DatabaseNodeImp.cpp +++ b/src/libxrpl/nodestore/DatabaseNodeImp.cpp @@ -29,7 +29,7 @@ DatabaseNodeImp::fetchNodeObject( bool duplicate) { std::shared_ptr nodeObject = nullptr; - Status status; + Status status = ok; try { diff --git a/src/libxrpl/nodestore/DatabaseRotatingImp.cpp b/src/libxrpl/nodestore/DatabaseRotatingImp.cpp index e058fa76ac..aa04b17b33 100644 --- a/src/libxrpl/nodestore/DatabaseRotatingImp.cpp +++ b/src/libxrpl/nodestore/DatabaseRotatingImp.cpp @@ -33,7 +33,7 @@ DatabaseRotatingImp::rotate( // deleted. std::shared_ptr oldArchiveBackend; { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); archiveBackend_->setDeletePath(); oldArchiveBackend = std::move(archiveBackend_); @@ -50,14 +50,14 @@ DatabaseRotatingImp::rotate( std::string DatabaseRotatingImp::getName() const { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return writableBackend_->getName(); } std::int32_t DatabaseRotatingImp::getWriteLoad() const { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return writableBackend_->getWriteLoad(); } @@ -65,7 +65,7 @@ void DatabaseRotatingImp::importDatabase(Database& source) { auto const backend = [&] { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return writableBackend_; }(); @@ -75,7 +75,7 @@ DatabaseRotatingImp::importDatabase(Database& source) void DatabaseRotatingImp::sync() { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); writableBackend_->sync(); } @@ -85,7 +85,7 @@ DatabaseRotatingImp::store(NodeObjectType type, Blob&& data, uint256 const& hash auto nObj = NodeObject::createObject(type, std::move(data), hash); auto const backend = [&] { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return writableBackend_; }(); @@ -101,7 +101,7 @@ DatabaseRotatingImp::fetchNodeObject( bool duplicate) { auto fetch = [&](std::shared_ptr const& backend) { - Status status; + Status status = ok; std::shared_ptr nodeObject; try { @@ -133,7 +133,7 @@ DatabaseRotatingImp::fetchNodeObject( std::shared_ptr nodeObject; auto [writable, archive] = [&] { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return std::make_pair(writableBackend_, archiveBackend_); }(); @@ -147,7 +147,7 @@ DatabaseRotatingImp::fetchNodeObject( { { // Refresh the writable backend pointer - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); writable = writableBackend_; } @@ -167,7 +167,7 @@ void DatabaseRotatingImp::for_each(std::function)> f) { auto [writable, archive] = [&] { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return std::make_pair(writableBackend_, archiveBackend_); }(); diff --git a/src/libxrpl/nodestore/ManagerImp.cpp b/src/libxrpl/nodestore/ManagerImp.cpp index b749a329ff..7925ccdb91 100644 --- a/src/libxrpl/nodestore/ManagerImp.cpp +++ b/src/libxrpl/nodestore/ManagerImp.cpp @@ -55,7 +55,7 @@ ManagerImp::make_Backend( missing_backend(); auto factory{find(type)}; - if (!factory) + if (factory == nullptr) { missing_backend(); } @@ -80,14 +80,14 @@ ManagerImp::make_Database( void ManagerImp::insert(Factory& factory) { - std::lock_guard _(mutex_); + std::lock_guard const _(mutex_); list_.push_back(&factory); } void ManagerImp::erase(Factory& factory) { - std::lock_guard _(mutex_); + std::lock_guard const _(mutex_); auto const iter = std::find_if( list_.begin(), list_.end(), [&factory](Factory* other) { return other == &factory; }); XRPL_ASSERT(iter != list_.end(), "xrpl::NodeStore::ManagerImp::erase : valid input"); @@ -97,7 +97,7 @@ ManagerImp::erase(Factory& factory) Factory* ManagerImp::find(std::string const& name) { - std::lock_guard _(mutex_); + std::lock_guard const _(mutex_); auto const iter = std::find_if(list_.begin(), list_.end(), [&name](Factory* other) { return boost::iequals(name, other->getName()); }); diff --git a/src/libxrpl/nodestore/backend/MemoryFactory.cpp b/src/libxrpl/nodestore/backend/MemoryFactory.cpp index b11d90610a..a245af3030 100644 --- a/src/libxrpl/nodestore/backend/MemoryFactory.cpp +++ b/src/libxrpl/nodestore/backend/MemoryFactory.cpp @@ -45,7 +45,7 @@ public: MemoryDB& open(std::string const& path) { - std::lock_guard _(mutex_); + std::lock_guard const _(mutex_); auto const result = map_.emplace(std::piecewise_construct, std::make_tuple(path), std::make_tuple()); MemoryDB& db = result.first->second; @@ -120,9 +120,9 @@ public: { XRPL_ASSERT(db_, "xrpl::NodeStore::MemoryBackend::fetch : non-null database"); - std::lock_guard _(db_->mutex); + std::lock_guard const _(db_->mutex); - Map::iterator iter = db_->table.find(hash); + Map::iterator const iter = db_->table.find(hash); if (iter == db_->table.end()) { pObject->reset(); @@ -140,11 +140,15 @@ public: for (auto const& h : hashes) { std::shared_ptr nObj; - Status status = fetch(h, &nObj); + Status const status = fetch(h, &nObj); if (status != ok) + { results.push_back({}); + } else + { results.push_back(nObj); + } } return {results, ok}; @@ -154,7 +158,7 @@ public: store(std::shared_ptr const& object) override { XRPL_ASSERT(db_, "xrpl::NodeStore::MemoryBackend::store : non-null database"); - std::lock_guard _(db_->mutex); + std::lock_guard const _(db_->mutex); db_->table.emplace(object->getHash(), object); } diff --git a/src/libxrpl/nodestore/backend/NuDBFactory.cpp b/src/libxrpl/nodestore/backend/NuDBFactory.cpp index c79938bcf8..424778f280 100644 --- a/src/libxrpl/nodestore/backend/NuDBFactory.cpp +++ b/src/libxrpl/nodestore/backend/NuDBFactory.cpp @@ -181,7 +181,7 @@ public: Status fetch(uint256 const& hash, std::shared_ptr* pno) override { - Status status; + Status status = ok; pno->reset(); nudb::error_code ec; db_.fetch( @@ -214,11 +214,15 @@ public: for (auto const& h : hashes) { std::shared_ptr nObj; - Status status = fetch(h, &nObj); + Status const status = fetch(h, &nObj); if (status != ok) + { results.push_back({}); + } else + { results.push_back(nObj); + } } return {results, ok}; @@ -227,7 +231,7 @@ public: void do_insert(std::shared_ptr const& no) { - EncodedBlob e(no); + EncodedBlob const e(no); nudb::error_code ec; nudb::detail::buffer bf; auto const result = nodeobject_compress(e.getData(), e.getSize(), bf); @@ -239,7 +243,7 @@ public: void store(std::shared_ptr const& no) override { - BatchWriteReport report; + BatchWriteReport report{}; report.writeCount = 1; auto const start = std::chrono::steady_clock::now(); do_insert(no); @@ -251,7 +255,7 @@ public: void storeBatch(Batch const& batch) override { - BatchWriteReport report; + BatchWriteReport report{}; report.writeCount = batch.size(); auto const start = std::chrono::steady_clock::now(); for (auto const& e : batch) @@ -349,7 +353,7 @@ private: auto const kp = (folder / "nudb.key").string(); std::size_t const defaultSize = nudb::block_size(kp); // Default 4K from NuDB - std::size_t blockSize = defaultSize; + std::size_t const blockSize = defaultSize; std::string blockSizeStr; if (!get_if_exists(keyValues, "nudb_block_size", blockSizeStr)) @@ -430,7 +434,7 @@ public: void registerNuDBFactory(Manager& manager) { - static NuDBFactory instance{manager}; + static NuDBFactory const instance{manager}; } } // namespace NodeStore diff --git a/src/libxrpl/nodestore/backend/NullFactory.cpp b/src/libxrpl/nodestore/backend/NullFactory.cpp index ab5b7d0117..617dfd893d 100644 --- a/src/libxrpl/nodestore/backend/NullFactory.cpp +++ b/src/libxrpl/nodestore/backend/NullFactory.cpp @@ -117,7 +117,7 @@ public: void registerNullFactory(Manager& manager) { - static NullFactory instance{manager}; + static NullFactory const instance{manager}; } } // namespace NodeStore diff --git a/src/libxrpl/nodestore/backend/RocksDBFactory.cpp b/src/libxrpl/nodestore/backend/RocksDBFactory.cpp index 01bc74f5ed..4ce7c3f10c 100644 --- a/src/libxrpl/nodestore/backend/RocksDBFactory.cpp +++ b/src/libxrpl/nodestore/backend/RocksDBFactory.cpp @@ -37,18 +37,17 @@ public: static void thread_entry(void* ptr) { - ThreadParams* const p(reinterpret_cast(ptr)); - void (*f)(void*) = p->f; + ThreadParams const* const p(reinterpret_cast(ptr)); + auto const f = p->f; + void* a(p->a); delete p; static std::atomic n; std::size_t const id(++n); - std::stringstream ss; - ss << "rocksdb #" << id; - beast::setCurrentThreadName(ss.str()); + beast::setCurrentThreadName("rocksdb #" + std::to_string(id)); - (*f)(a); + f(a); } void @@ -89,7 +88,7 @@ public: rocksdb::BlockBasedTableOptions table_options; m_options.env = env; - bool hard_set = keyValues.exists("hard_set") && get(keyValues, "hard_set"); + bool const hard_set = keyValues.exists("hard_set") && get(keyValues, "hard_set"); if (keyValues.exists("cache_mb")) { @@ -162,12 +161,14 @@ public: if (keyValues.exists("bbt_options")) { - rocksdb::ConfigOptions config_options; + rocksdb::ConfigOptions const config_options; auto const s = rocksdb::GetBlockBasedTableOptionsFromString( config_options, table_options, get(keyValues, "bbt_options"), &table_options); if (!s.ok()) + { Throw( std::string("Unable to set RocksDB bbt_options: ") + s.ToString()); + } } m_options.table_factory.reset(NewBlockBasedTableFactory(table_options)); @@ -177,8 +178,10 @@ public: auto const s = rocksdb::GetOptionsFromString(m_options, get(keyValues, "options"), &m_options); if (!s.ok()) + { Throw( std::string("Unable to set RocksDB options: ") + s.ToString()); + } } std::string s1, s2; @@ -208,10 +211,12 @@ public: } rocksdb::DB* db = nullptr; m_options.create_if_missing = createIfMissing; - rocksdb::Status status = rocksdb::DB::Open(m_options, m_name, &db); - if (!status.ok() || !db) + rocksdb::Status const status = rocksdb::DB::Open(m_options, m_name, &db); + if (!status.ok() || (db == nullptr)) + { Throw( std::string("Unable to open/create RocksDB: ") + status.ToString()); + } m_db.reset(db); } @@ -229,7 +234,7 @@ public: m_db.reset(); if (m_deletePath) { - boost::filesystem::path dir = m_name; + boost::filesystem::path const dir = m_name; boost::filesystem::remove_all(dir); } } @@ -256,7 +261,7 @@ public: std::string string; - rocksdb::Status getStatus = m_db->Get(options, slice, &string); + rocksdb::Status const getStatus = m_db->Get(options, slice, &string); if (getStatus.ok()) { @@ -302,11 +307,15 @@ public: for (auto const& h : hashes) { std::shared_ptr nObj; - Status status = fetch(h, &nObj); + Status const status = fetch(h, &nObj); if (status != ok) + { results.push_back({}); + } else + { results.push_back(nObj); + } } return {results, ok}; @@ -329,7 +338,7 @@ public: for (auto const& e : batch) { - EncodedBlob encoded(e); + EncodedBlob const encoded(e); wb.Put( rocksdb::Slice(std::bit_cast(encoded.getKey()), m_keyBytes), @@ -446,7 +455,7 @@ public: void registerRocksDBFactory(Manager& manager) { - static RocksDBFactory instance{manager}; + static RocksDBFactory const instance{manager}; } } // namespace NodeStore diff --git a/src/libxrpl/protocol/AccountID.cpp b/src/libxrpl/protocol/AccountID.cpp index 4edbf5c0e7..2c64457c87 100644 --- a/src/libxrpl/protocol/AccountID.cpp +++ b/src/libxrpl/protocol/AccountID.cpp @@ -55,7 +55,7 @@ public: packed_spinlock sl(locks_, index % 64); { - std::lock_guard lock(sl); + std::lock_guard const lock(sl); // The check against the first character of the encoding ensures // that we don't mishandle the case of the all-zero account: @@ -68,7 +68,7 @@ public: XRPL_ASSERT(ret.size() <= 38, "xrpl::detail::AccountIdCache : maximum result size"); { - std::lock_guard lock(sl); + std::lock_guard const lock(sl); cache_[index].id = id; std::strcpy(cache_[index].encoding, ret.c_str()); } diff --git a/src/libxrpl/protocol/BuildInfo.cpp b/src/libxrpl/protocol/BuildInfo.cpp index c7531f8376..543da615cf 100644 --- a/src/libxrpl/protocol/BuildInfo.cpp +++ b/src/libxrpl/protocol/BuildInfo.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -80,7 +81,7 @@ getVersionString() std::string const& getFullVersionString() { - static std::string const value = "rippled-" + getVersionString(); + static std::string const value = systemName() + "-" + getVersionString(); return value; } @@ -112,7 +113,7 @@ encodeSoftwareVersion(std::string_view versionStr) { std::uint8_t x = 0; - for (auto id : v.preReleaseIdentifiers) + for (auto const& id : v.preReleaseIdentifiers) { auto parsePreRelease = [](std::string_view identifier, std::string_view prefix, @@ -139,7 +140,7 @@ encodeSoftwareVersion(std::string_view versionStr) if (x == 0) x = parsePreRelease(id, "b", 0x40, 0, 63); - if (x & 0xC0) + if ((x & 0xC0) != 0) { c |= static_cast(x) << 16; break; @@ -159,7 +160,7 @@ getEncodedVersion() } bool -isRippledVersion(std::uint64_t version) +isXrpldVersion(std::uint64_t version) { return (version & implementationVersionIdentifierMask) == implementationVersionIdentifier; } @@ -167,7 +168,7 @@ isRippledVersion(std::uint64_t version) bool isNewerVersion(std::uint64_t version) { - if (isRippledVersion(version)) + if (isXrpldVersion(version)) return version > getEncodedVersion(); return false; } diff --git a/src/libxrpl/protocol/ErrorCodes.cpp b/src/libxrpl/protocol/ErrorCodes.cpp index 33ec869db6..407e1ab3f3 100644 --- a/src/libxrpl/protocol/ErrorCodes.cpp +++ b/src/libxrpl/protocol/ErrorCodes.cpp @@ -205,9 +205,7 @@ make_error(error_code_i code, std::string const& message) bool contains_error(Json::Value const& json) { - if (json.isObject() && json.isMember(jss::error)) - return true; - return false; + return json.isObject() && json.isMember(jss::error); } int diff --git a/src/libxrpl/protocol/Feature.cpp b/src/libxrpl/protocol/Feature.cpp index 28ff1547df..1762d7d22d 100644 --- a/src/libxrpl/protocol/Feature.cpp +++ b/src/libxrpl/protocol/Feature.cpp @@ -209,7 +209,7 @@ FeatureCollections::getRegisteredFeature(std::string const& name) const XRPL_ASSERT( readOnly.load(), "xrpl::FeatureCollections::getRegisteredFeature : startup completed"); Feature const* feature = getByName(name); - if (feature) + if (feature != nullptr) return feature->feature; return std::nullopt; } @@ -229,7 +229,7 @@ FeatureCollections::registerFeature(std::string const& name, Supported support, support == Supported::yes || vote == VoteBehavior::DefaultNo, "Invalid feature parameters. Must be supported to be up-voted."); Feature const* i = getByName(name); - if (!i) + if (i == nullptr) { check(features.size() < detail::numFeatures, "More features defined than allocated."); @@ -250,18 +250,22 @@ FeatureCollections::registerFeature(std::string const& name, Supported support, supported.emplace(name, vote); if (vote == VoteBehavior::DefaultYes) + { ++upVotes; + } else + { ++downVotes; + } } check(upVotes + downVotes == supported.size(), "Feature counting logic broke"); check(supported.size() <= features.size(), "More supported features than defined features"); check(features.size() == all.size(), "The 'all' features list is populated incorrectly"); return f; } - else - // Each feature should only be registered once - LogicError("Duplicate feature registration"); + + // Each feature should only be registered once + LogicError("Duplicate feature registration"); } /** Tell FeatureCollections when registration is complete. */ @@ -279,7 +283,7 @@ FeatureCollections::featureToBitsetIndex(uint256 const& f) const readOnly.load(), "xrpl::FeatureCollections::featureToBitsetIndex : startup completed"); Feature const* feature = getByFeature(f); - if (!feature) + if (feature == nullptr) LogicError("Invalid Feature ID"); return getIndex(*feature); @@ -299,10 +303,10 @@ FeatureCollections::featureToName(uint256 const& f) const { XRPL_ASSERT(readOnly.load(), "xrpl::FeatureCollections::featureToName : startup completed"); Feature const* feature = getByFeature(f); - return feature ? feature->name : to_string(f); + return (feature != nullptr) ? feature->name : to_string(f); } -static FeatureCollections featureCollections; +FeatureCollections featureCollections; } // namespace @@ -395,10 +399,20 @@ featureToName(uint256 const& f) #pragma push_macro("XRPL_RETIRE_FIX") #undef XRPL_RETIRE_FIX +consteval auto +enforceValidFeatureName(auto fn) -> char const* +{ + static_assert(validFeatureName(fn), "Invalid feature name"); + static_assert(validFeatureNameSize(fn), "Invalid feature name size"); + return fn(); +} + #define XRPL_FEATURE(name, supported, vote) \ - uint256 const feature##name = registerFeature(#name, supported, vote); + uint256 const feature##name = \ + registerFeature(enforceValidFeatureName([] { return #name; }), supported, vote); #define XRPL_FIX(name, supported, vote) \ - uint256 const fix##name = registerFeature("fix" #name, supported, vote); + uint256 const fix##name = \ + registerFeature(enforceValidFeatureName([] { return "fix" #name; }), supported, vote); // clang-format off #define XRPL_RETIRE_FEATURE(name) \ diff --git a/src/libxrpl/protocol/IOUAmount.cpp b/src/libxrpl/protocol/IOUAmount.cpp index eba78e6051..338f2c2760 100644 --- a/src/libxrpl/protocol/IOUAmount.cpp +++ b/src/libxrpl/protocol/IOUAmount.cpp @@ -186,7 +186,7 @@ mulRatio(IOUAmount const& amt, std::uint32_t num, std::uint32_t den, bool roundU { using namespace boost::multiprecision; - if (!den) + if (den == 0u) Throw("division by zero"); // A vector with the value 10^index for indexes from 0 to 29 diff --git a/src/libxrpl/protocol/Indexes.cpp b/src/libxrpl/protocol/Indexes.cpp index 45ab20f057..61a64b2a54 100644 --- a/src/libxrpl/protocol/Indexes.cpp +++ b/src/libxrpl/protocol/Indexes.cpp @@ -137,7 +137,7 @@ getQuality(uint256 const& uBase) uint256 getTicketIndex(AccountID const& account, std::uint32_t ticketSeq) { - return indexHash(LedgerNameSpace::TICKET, account, std::uint32_t(ticketSeq)); + return indexHash(LedgerNameSpace::TICKET, account, ticketSeq); } uint256 @@ -219,7 +219,7 @@ book_t::operator()(Book const& b) const Keylet line(AccountID const& id0, AccountID const& id1, Currency const& currency) noexcept { - // There is code in SetTrust that calls us with id0 == id1, to allow users + // There is code in TrustSet that calls us with id0 == id1, to allow users // to locate and delete such "weird" trustlines. If we remove that code, we // could enable this assert: // XRPL_ASSERT(id0 != id1, "xrpl::keylet::line : accounts must be diff --git a/src/libxrpl/protocol/InnerObjectFormats.cpp b/src/libxrpl/protocol/InnerObjectFormats.cpp index 8139ef0f55..8429c51aea 100644 --- a/src/libxrpl/protocol/InnerObjectFormats.cpp +++ b/src/libxrpl/protocol/InnerObjectFormats.cpp @@ -164,7 +164,7 @@ InnerObjectFormats::InnerObjectFormats() InnerObjectFormats const& InnerObjectFormats::getInstance() { - static InnerObjectFormats instance; + static InnerObjectFormats const instance; return instance; } @@ -172,7 +172,7 @@ SOTemplate const* InnerObjectFormats::findSOTemplateBySField(SField const& sField) const { auto itemPtr = findByType(sField.getCode()); - if (itemPtr) + if (itemPtr != nullptr) return &(itemPtr->getSOTemplate()); return nullptr; diff --git a/src/libxrpl/protocol/Issue.cpp b/src/libxrpl/protocol/Issue.cpp index 1a56007729..c73c6c8513 100644 --- a/src/libxrpl/protocol/Issue.cpp +++ b/src/libxrpl/protocol/Issue.cpp @@ -25,11 +25,17 @@ Issue::getText() const ret += "/"; if (isXRP(account)) + { ret += "0"; + } else if (account == noAccount()) + { ret += "1"; + } else + { ret += to_string(account); + } } return ret; diff --git a/src/libxrpl/protocol/LedgerFormats.cpp b/src/libxrpl/protocol/LedgerFormats.cpp index 30725f44a9..9f8bd6a2ba 100644 --- a/src/libxrpl/protocol/LedgerFormats.cpp +++ b/src/libxrpl/protocol/LedgerFormats.cpp @@ -40,7 +40,7 @@ LedgerFormats::LedgerFormats() LedgerFormats const& LedgerFormats::getInstance() { - static LedgerFormats instance; + static LedgerFormats const instance; return instance; } diff --git a/src/libxrpl/protocol/LedgerHeader.cpp b/src/libxrpl/protocol/LedgerHeader.cpp index 270833c6fa..38e7a7f6dd 100644 --- a/src/libxrpl/protocol/LedgerHeader.cpp +++ b/src/libxrpl/protocol/LedgerHeader.cpp @@ -1,7 +1,9 @@ #include #include +#include #include #include +#include namespace xrpl { @@ -51,4 +53,21 @@ deserializePrefixedHeader(Slice data, bool hasHash) return deserializeHeader(data + 4, hasHash); } +uint256 +calculateLedgerHash(LedgerHeader const& info) +{ + // VFALCO This has to match addRaw in View.h. + return sha512Half( + HashPrefix::ledgerMaster, + std::uint32_t(info.seq), + std::uint64_t(info.drops.drops()), + info.parentHash, + info.txHash, + info.accountHash, + std::uint32_t(info.parentCloseTime.time_since_epoch().count()), + std::uint32_t(info.closeTime.time_since_epoch().count()), + std::uint8_t(info.closeTimeResolution.count()), + std::uint8_t(info.closeFlags)); +} + } // namespace xrpl diff --git a/src/libxrpl/protocol/NFTokenID.cpp b/src/libxrpl/protocol/NFTokenID.cpp index b533fa53c0..f77a6f67df 100644 --- a/src/libxrpl/protocol/NFTokenID.cpp +++ b/src/libxrpl/protocol/NFTokenID.cpp @@ -30,7 +30,7 @@ canHaveNFTokenID(std::shared_ptr const& serializedTx, TxMeta const& return false; // if the transaction failed nothing could have been delivered. - if (transactionMeta.getResultTER() != tesSUCCESS) + if (!isTesSuccess(transactionMeta.getResultTER())) return false; return true; @@ -70,7 +70,7 @@ getNFTokenIDFromPage(TxMeta const& transactionMeta) // field changing, but no NFTs within that page changing. In this // case, there will be no previous NFTs and we need to skip. // However, there will always be NFTs listed in the final fields, - // as rippled outputs all fields in final fields even if they were + // as xrpld outputs all fields in final fields even if they were // not changed. STObject const& previousFields = node.peekAtField(sfPreviousFields).downcast(); @@ -154,12 +154,12 @@ insertNFTokenID( { std::vector result = getNFTokenIDFromDeletedOffer(transactionMeta); - if (result.size() > 0) + if (!result.empty()) response[jss::nftoken_id] = to_string(result.front()); } else if (type == ttNFTOKEN_CANCEL_OFFER) { - std::vector result = getNFTokenIDFromDeletedOffer(transactionMeta); + std::vector const result = getNFTokenIDFromDeletedOffer(transactionMeta); response[jss::nftoken_ids] = Json::Value(Json::arrayValue); for (auto const& nftID : result) diff --git a/src/libxrpl/protocol/NFTokenOfferID.cpp b/src/libxrpl/protocol/NFTokenOfferID.cpp index b1a08081c0..4af589a4e9 100644 --- a/src/libxrpl/protocol/NFTokenOfferID.cpp +++ b/src/libxrpl/protocol/NFTokenOfferID.cpp @@ -24,12 +24,12 @@ canHaveNFTokenOfferID( return false; TxType const tt = serializedTx->getTxnType(); - if (!(tt == ttNFTOKEN_MINT && serializedTx->isFieldPresent(sfAmount)) && + if ((tt != ttNFTOKEN_MINT || !serializedTx->isFieldPresent(sfAmount)) && tt != ttNFTOKEN_CREATE_OFFER) return false; // if the transaction failed nothing could have been delivered. - if (transactionMeta.getResultTER() != tesSUCCESS) + if (!isTesSuccess(transactionMeta.getResultTER())) return false; return true; diff --git a/src/libxrpl/protocol/Permissions.cpp b/src/libxrpl/protocol/Permissions.cpp index cce7ab5058..47fc0d28b6 100644 --- a/src/libxrpl/protocol/Permissions.cpp +++ b/src/libxrpl/protocol/Permissions.cpp @@ -68,10 +68,12 @@ Permission::Permission() }; for ([[maybe_unused]] auto const& permission : granularPermissionMap_) + { XRPL_ASSERT( permission.second > UINT16_MAX, "xrpl::Permission::granularPermissionMap_ : granular permission " "value must not exceed the maximum uint16_t value."); + } } Permission const& @@ -126,7 +128,7 @@ Permission::getGranularTxType(GranularPermissionType const& gpType) const return std::nullopt; } -std::optional> const +std::optional> Permission::getTxFeature(TxType txType) const { auto const txFeaturesIt = txFeatureMap_.find(txType); @@ -145,8 +147,10 @@ Permission::isDelegable(std::uint32_t const& permissionValue, Rules const& rules auto const granularPermission = getGranularName(static_cast(permissionValue)); if (granularPermission) + { // granular permissions are always allowed to be delegated return true; + } auto const txType = permissionToTxType(permissionValue); auto const it = delegableTx_.find(txType); @@ -172,13 +176,13 @@ Permission::isDelegable(std::uint32_t const& permissionValue, Rules const& rules } uint32_t -Permission::txToPermissionType(TxType const& type) const +Permission::txToPermissionType(TxType const& type) { return static_cast(type) + 1; } TxType -Permission::permissionToTxType(uint32_t const& value) const +Permission::permissionToTxType(uint32_t const& value) { return static_cast(value - 1); } diff --git a/src/libxrpl/protocol/PublicKey.cpp b/src/libxrpl/protocol/PublicKey.cpp index 3251cb84ac..fc63edd2fc 100644 --- a/src/libxrpl/protocol/PublicKey.cpp +++ b/src/libxrpl/protocol/PublicKey.cpp @@ -75,7 +75,7 @@ static std::string sliceToHex(Slice const& slice) { std::string s; - if (slice[0] & 0x80) + if ((slice[0] & 0x80) != 0) { s.reserve(2 * (slice.size() + 2)); s = "0x00"; @@ -130,11 +130,11 @@ ecdsaCanonicality(Slice const& sig) if (!r || !s || !p.empty()) return std::nullopt; - uint264 R(sliceToHex(*r)); + uint264 const R(sliceToHex(*r)); if (R >= G) return std::nullopt; - uint264 S(sliceToHex(*s)); + uint264 const S(sliceToHex(*s)); if (S >= G) return std::nullopt; @@ -171,9 +171,11 @@ ed25519Canonical(Slice const& sig) PublicKey::PublicKey(Slice const& slice) { if (slice.size() < size_) + { LogicError( "PublicKey::PublicKey - Input slice cannot be an undersized " "buffer"); + } if (!publicKeyType(slice)) LogicError("PublicKey::PublicKey invalid type"); @@ -270,7 +272,7 @@ verify(PublicKey const& publicKey, Slice const& m, Slice const& sig) noexcept { return verifyDigest(publicKey, sha512Half(m), sig); } - else if (*type == KeyType::ed25519) + if (*type == KeyType::ed25519) { if (!ed25519Canonical(sig)) return false; diff --git a/src/libxrpl/protocol/QualityFunction.cpp b/src/libxrpl/protocol/QualityFunction.cpp index ebc59dcc83..1774603a61 100644 --- a/src/libxrpl/protocol/QualityFunction.cpp +++ b/src/libxrpl/protocol/QualityFunction.cpp @@ -31,7 +31,7 @@ QualityFunction::outFromAvgQ(Quality const& quality) { if (m_ != 0 && quality.rate() != beast::zero) { - saveNumberRoundMode rm(Number::setround(Number::rounding_mode::upward)); + saveNumberRoundMode const rm(Number::setround(Number::rounding_mode::upward)); auto const out = (1 / quality.rate() - b_) / m_; if (out <= 0) return std::nullopt; diff --git a/src/libxrpl/protocol/Rules.cpp b/src/libxrpl/protocol/Rules.cpp index 5a97a33185..fd54a1eff2 100644 --- a/src/libxrpl/protocol/Rules.cpp +++ b/src/libxrpl/protocol/Rules.cpp @@ -39,7 +39,7 @@ 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 enableLargeNumbers = + bool const enableLargeNumbers = !r || (r->enabled(featureSingleAssetVault) || r->enabled(featureLendingProtocol)); Number::setMantissaScale(enableLargeNumbers ? MantissaRange::large : MantissaRange::small); @@ -77,9 +77,9 @@ public: bool enabled(uint256 const& feature) const { - if (presets_.count(feature) > 0) + if (presets_.contains(feature)) return true; - return set_.count(feature) > 0; + return set_.contains(feature); } bool diff --git a/src/libxrpl/protocol/STAccount.cpp b/src/libxrpl/protocol/STAccount.cpp index b85341daf3..2dc83cb591 100644 --- a/src/libxrpl/protocol/STAccount.cpp +++ b/src/libxrpl/protocol/STAccount.cpp @@ -16,7 +16,7 @@ namespace xrpl { -STAccount::STAccount() : STBase(), value_(beast::zero), default_(true) +STAccount::STAccount() : value_(beast::zero), default_(true) { } @@ -24,7 +24,7 @@ STAccount::STAccount(SField const& n) : STBase(n), value_(beast::zero), default_ { } -STAccount::STAccount(SField const& n, Buffer&& v) : STAccount(n) +STAccount::STAccount(SField const& n, Buffer const& v) : STAccount(n) { if (v.empty()) return; // Zero is a valid size for a defaulted STAccount. @@ -84,7 +84,7 @@ bool STAccount::isEquivalent(STBase const& t) const { auto const* const tPtr = dynamic_cast(&t); - return tPtr && (default_ == tPtr->default_) && (value_ == tPtr->value_); + return (tPtr != nullptr) && (default_ == tPtr->default_) && (value_ == tPtr->value_); } bool diff --git a/src/libxrpl/protocol/STAmount.cpp b/src/libxrpl/protocol/STAmount.cpp index 9503da57a2..cc935cff61 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -152,9 +152,9 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name) value &= ~(1023ull << (64 - 10)); - if (value) + if (value != 0u) { - bool isNegative = (offset & 256) == 0; + bool const isNegative = (offset & 256) == 0; offset = (offset & 255) - 97; // center the range if (value < cMinValue || value > cMaxValue || offset < cMinOffset || offset > cMaxOffset) @@ -223,9 +223,13 @@ STAmount::STAmount(XRPAmount const& amount) : mAsset(xrpIssue()), mOffset(0), mIsNegative(amount < beast::zero) { if (mIsNegative) + { mValue = unsafe_cast(-amount.drops()); + } else + { mValue = unsafe_cast(amount.drops()); + } canonicalize(); } @@ -305,9 +309,13 @@ STAmount::operator=(IOUAmount const& iou) mOffset = iou.exponent(); mIsNegative = iou < beast::zero; if (mIsNegative) + { mValue = static_cast(-iou.mantissa()); + } else + { mValue = static_cast(iou.mantissa()); + } return *this; } @@ -402,7 +410,7 @@ operator+(STAmount const& v1, STAmount const& v2) // This addition cannot overflow an std::int64_t. It can overflow an // STAmount and the constructor will throw. - std::int64_t fv = vv1 + vv2; + std::int64_t const fv = vv1 + vv2; if ((fv >= -10) && (fv <= 10)) return {v1.getFName(), v1.asset()}; @@ -446,13 +454,13 @@ getRate(STAmount const& offerOut, STAmount const& offerIn) try { - STAmount r = divide(offerIn, offerOut, noIssue()); + STAmount const r = divide(offerIn, offerOut, noIssue()); if (r == beast::zero) // offer is too good return 0; XRPL_ASSERT( (r.exponent() >= -100) && (r.exponent() <= 155), "xrpl::getRate : exponent inside range"); - std::uint64_t ret = r.exponent() + 100; + std::uint64_t const ret = r.exponent() + 100; return (ret << (64 - 8)) | r.mantissa(); } catch (...) @@ -494,17 +502,14 @@ canAdd(STAmount const& a, STAmount const& b) // XRP case (overflow & underflow check) if (isXRP(a) && isXRP(b)) { - XRPAmount A = a.xrp(); - XRPAmount B = b.xrp(); + XRPAmount const A = a.xrp(); + XRPAmount const B = b.xrp(); - if ((B > XRPAmount{0} && + return !( + (B > XRPAmount{0} && A > XRPAmount{std::numeric_limits::max()} - B) || (B < XRPAmount{0} && - A < XRPAmount{std::numeric_limits::min()} - B)) - { - return false; - } - return true; + A < XRPAmount{std::numeric_limits::min()} - B)); } // IOU case (precision check) @@ -512,25 +517,21 @@ canAdd(STAmount const& a, STAmount const& b) { static STAmount const one{IOUAmount{1, 0}, noIssue()}; static STAmount const maxLoss{IOUAmount{1, -4}, noIssue()}; - STAmount lhs = divide((a - b) + b, a, noIssue()) - one; - STAmount rhs = divide((b - a) + a, b, noIssue()) - one; + STAmount const lhs = divide((a - b) + b, a, noIssue()) - one; + STAmount const rhs = divide((b - a) + a, b, noIssue()) - one; return ((rhs.negative() ? -rhs : rhs) + (lhs.negative() ? -lhs : lhs)) <= maxLoss; } // MPT (overflow & underflow check) if (a.holds() && b.holds()) { - MPTAmount A = a.mpt(); - MPTAmount B = b.mpt(); - if ((B > MPTAmount{0} && + MPTAmount const A = a.mpt(); + MPTAmount const B = b.mpt(); + return !( + (B > MPTAmount{0} && A > MPTAmount{std::numeric_limits::max()} - B) || (B < MPTAmount{0} && - A < MPTAmount{std::numeric_limits::min()} - B)) - { - return false; - } - - return true; + A < MPTAmount{std::numeric_limits::min()} - B)); } // LCOV_EXCL_START UNREACHABLE("STAmount::canAdd : unexpected STAmount type"); @@ -569,8 +570,8 @@ canSubtract(STAmount const& a, STAmount const& b) // XRP case (underflow & overflow check) if (isXRP(a) && isXRP(b)) { - XRPAmount A = a.xrp(); - XRPAmount B = b.xrp(); + XRPAmount const A = a.xrp(); + XRPAmount const B = b.xrp(); // Check for underflow if (B > XRPAmount{0} && A < B) return false; @@ -592,8 +593,8 @@ canSubtract(STAmount const& a, STAmount const& b) // MPT case (underflow & overflow check) if (a.holds() && b.holds()) { - MPTAmount A = a.mpt(); - MPTAmount B = b.mpt(); + MPTAmount const A = a.mpt(); + MPTAmount const B = b.mpt(); // Underflow check if (B > MPTAmount{0} && A < B) @@ -722,9 +723,13 @@ STAmount::getText() const // Assemble the output: if (pre_from == pre_to) + { ret.append(1, '0'); + } else + { ret.append(pre_from, pre_to); + } if (post_to != post_from) { @@ -751,9 +756,13 @@ STAmount::add(Serializer& s) const XRPL_ASSERT(mOffset == 0, "xrpl::STAmount::add : zero offset"); if (!mIsNegative) + { s.add64(mValue | cPositive); + } else + { s.add64(mValue); + } } else if (mAsset.holds()) { @@ -767,11 +776,17 @@ STAmount::add(Serializer& s) const else { if (*this == beast::zero) + { s.add64(cIssuedCurrency); - else if (mIsNegative) // 512 = not native + } + else if (mIsNegative) + { // 512 = not native s.add64(mValue | (static_cast(mOffset + 512 + 97) << (64 - 10))); - else // 256 = positive + } + else + { // 256 = positive s.add64(mValue | (static_cast(mOffset + 512 + 256 + 97) << (64 - 10))); + } s.addBitString(mAsset.get().currency); s.addBitString(mAsset.get().account); } @@ -781,7 +796,7 @@ bool STAmount::isEquivalent(STBase const& t) const { STAmount const* v = dynamic_cast(&t); - return v && (*v == *this); + return (v != nullptr) && (*v == *this); } bool @@ -832,18 +847,24 @@ STAmount::canonicalize() if (getSTNumberSwitchover()) { - Number num(mIsNegative, mValue, mOffset, Number::unchecked{}); + Number const num(mIsNegative, mValue, mOffset, Number::unchecked{}); auto set = [&](auto const& val) { auto const value = val.value(); mIsNegative = value < 0; mValue = mIsNegative ? -value : value; }; if (native()) + { set(XRPAmount{num}); + } else if (mAsset.holds()) + { set(MPTAmount{num}); + } else + { Throw("Unknown integral asset type"); + } mOffset = 0; } else @@ -859,9 +880,13 @@ STAmount::canonicalize() // N.B. do not move the overflow check to after the // multiplication if (native() && mValue > cMaxNativeN) + { Throw("Native currency amount out of range"); + } else if (!native() && mValue > maxMPTokenAmount) + { Throw("MPT amount out of range"); + } mValue *= 10; --mOffset; @@ -869,9 +894,13 @@ STAmount::canonicalize() } if (native() && mValue > cMaxNativeN) + { Throw("Native currency amount out of range"); + } else if (!native() && mValue > maxMPTokenAmount) + { Throw("MPT amount out of range"); + } return; } @@ -948,8 +977,8 @@ amountFromQuality(std::uint64_t rate) if (rate == 0) return STAmount(noIssue()); - std::uint64_t mantissa = rate & ~(255ull << (64 - 8)); - int exponent = static_cast(rate >> (64 - 8)) - 100; + std::uint64_t const mantissa = rate & ~(255ull << (64 - 8)); + int const exponent = static_cast(rate >> (64 - 8)) - 100; return STAmount(noIssue(), mantissa, exponent); } @@ -1445,7 +1474,7 @@ roundToScale(STAmount const& value, std::int32_t scale, Number::rounding_mode ro STAmount const referenceValue{value.asset(), STAmount::cMinValue, scale, value.negative()}; - NumberRoundModeGuard mg(rounding); + NumberRoundModeGuard const mg(rounding); // With an IOU, the 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 @@ -1487,8 +1516,8 @@ mulRoundImpl(STAmount const& v1, STAmount const& v2, Asset const& asset, bool ro if (v1.native() && v2.native() && xrp) { - std::uint64_t minV = std::min(getSNValue(v1), getSNValue(v2)); - std::uint64_t maxV = std::max(getSNValue(v1), getSNValue(v2)); + std::uint64_t const minV = std::min(getSNValue(v1), getSNValue(v2)); + std::uint64_t const maxV = std::max(getSNValue(v1), getSNValue(v2)); if (minV > 3000000000ull) // sqrt(cMaxNative) Throw("Native value overflow"); diff --git a/src/libxrpl/protocol/STArray.cpp b/src/libxrpl/protocol/STArray.cpp index 8e7827cccc..91acc9ddd3 100644 --- a/src/libxrpl/protocol/STArray.cpp +++ b/src/libxrpl/protocol/STArray.cpp @@ -44,7 +44,7 @@ STArray::STArray(SerialIter& sit, SField const& f, int depth) : STBase(f) { while (!sit.empty()) { - int type, field; + int type = 0, field = 0; sit.getFieldID(type, field); if ((type == STI_ARRAY) && (field == 1)) diff --git a/src/libxrpl/protocol/STBase.cpp b/src/libxrpl/protocol/STBase.cpp index f6b1dcec58..3c5c34ae4e 100644 --- a/src/libxrpl/protocol/STBase.cpp +++ b/src/libxrpl/protocol/STBase.cpp @@ -23,6 +23,9 @@ STBase::STBase(SField const& n) : fName(&n) STBase& STBase::operator=(STBase const& t) { + if (this == &t) + return *this; + if (!fName->isUseful()) fName = t.fName; return *this; diff --git a/src/libxrpl/protocol/STBlob.cpp b/src/libxrpl/protocol/STBlob.cpp index 1a9dae7771..e7d45a698d 100644 --- a/src/libxrpl/protocol/STBlob.cpp +++ b/src/libxrpl/protocol/STBlob.cpp @@ -53,7 +53,7 @@ bool STBlob::isEquivalent(STBase const& t) const { STBlob const* v = dynamic_cast(&t); - return v && (value_ == v->value_); + return (v != nullptr) && (value_ == v->value_); } bool diff --git a/src/libxrpl/protocol/STCurrency.cpp b/src/libxrpl/protocol/STCurrency.cpp index 1616de59b9..3ca7b60d6b 100644 --- a/src/libxrpl/protocol/STCurrency.cpp +++ b/src/libxrpl/protocol/STCurrency.cpp @@ -56,7 +56,7 @@ bool STCurrency::isEquivalent(STBase const& t) const { STCurrency const* v = dynamic_cast(&t); - return v && (*v == *this); + return (v != nullptr) && (*v == *this); } bool diff --git a/src/libxrpl/protocol/STIssue.cpp b/src/libxrpl/protocol/STIssue.cpp index 5887b5c21f..5acf4abcb6 100644 --- a/src/libxrpl/protocol/STIssue.cpp +++ b/src/libxrpl/protocol/STIssue.cpp @@ -38,7 +38,7 @@ STIssue::STIssue(SerialIter& sit, SField const& name) : STBase{name} // - 160 bits MPT issuer account // - 160 bits black hole account // - 32 bits sequence - AccountID account = static_cast(sit.get160()); + AccountID const account = static_cast(sit.get160()); // MPT if (noAccount() == account) { @@ -50,7 +50,7 @@ STIssue::STIssue(SerialIter& sit, SField const& name) : STBase{name} mptID.data() + sizeof(sequence), currencyOrAccount.data(), sizeof(currencyOrAccount)); - MPTIssue issue{mptID}; + MPTIssue const issue{mptID}; asset_ = issue; } else @@ -100,7 +100,7 @@ STIssue::add(Serializer& s) const auto const& issue = asset_.get(); s.addBitString(issue.getIssuer()); s.addBitString(noAccount()); - std::uint32_t sequence; + std::uint32_t sequence = 0; memcpy(&sequence, issue.getMptID().data(), sizeof(sequence)); s.add32(sequence); } @@ -110,7 +110,7 @@ bool STIssue::isEquivalent(STBase const& t) const { STIssue const* v = dynamic_cast(&t); - return v && (*v == *this); + return (v != nullptr) && (*v == *this); } bool diff --git a/src/libxrpl/protocol/STLedgerEntry.cpp b/src/libxrpl/protocol/STLedgerEntry.cpp index 8d0d56a737..5a80559c98 100644 --- a/src/libxrpl/protocol/STLedgerEntry.cpp +++ b/src/libxrpl/protocol/STLedgerEntry.cpp @@ -33,9 +33,11 @@ STLedgerEntry::STLedgerEntry(Keylet const& k) : STObject(sfLedgerEntry), key_(k. auto const format = LedgerFormats::getInstance().findByType(type_); if (format == nullptr) + { Throw( "Attempt to create a SLE of unknown type " + std::to_string(safe_cast(k.type))); + } set(format->getSOTemplate()); @@ -43,14 +45,14 @@ STLedgerEntry::STLedgerEntry(Keylet const& k) : STObject(sfLedgerEntry), key_(k. } STLedgerEntry::STLedgerEntry(SerialIter& sit, uint256 const& index) - : STObject(sfLedgerEntry), key_(index) + : STObject(sfLedgerEntry), key_(index), type_(ltANY) { set(sit); setSLEType(); } STLedgerEntry::STLedgerEntry(STObject const& object, uint256 const& index) - : STObject(object), key_(index) + : STObject(object), key_(index), type_(ltANY) { setSLEType(); } @@ -118,8 +120,10 @@ STLedgerEntry::getJson(JsonOptions options) const ret[jss::index] = to_string(key_); if (getType() == ltMPTOKEN_ISSUANCE) + { ret[jss::mpt_issuance_id] = to_string(makeMptID(getFieldU32(sfSequence), getAccountID(sfIssuer))); + } return ret; } @@ -132,7 +136,7 @@ STLedgerEntry::isThreadedType(Rules const& rules) const // Exclude PrevTxnID/PrevTxnLgrSeq if the fixPreviousTxnID amendment is not // enabled and the ledger object type is in the above set bool const excludePrevTxnID = !rules.enabled(fixPreviousTxnID) && - std::count(newPreviousTxnIDTypes.cbegin(), newPreviousTxnIDTypes.cend(), type_); + (std::count(newPreviousTxnIDTypes.cbegin(), newPreviousTxnIDTypes.cend(), type_) != 0); return !excludePrevTxnID && getFieldIndex(sfPreviousTxnID) != -1; } @@ -143,7 +147,7 @@ STLedgerEntry::thread( uint256& prevTxID, std::uint32_t& prevLedgerID) { - uint256 oldPrevTxID = getFieldH256(sfPreviousTxnID); + uint256 const oldPrevTxID = getFieldH256(sfPreviousTxnID); JLOG(debugLog().info()) << "Thread Tx:" << txID << " prev:" << oldPrevTxID; diff --git a/src/libxrpl/protocol/STNumber.cpp b/src/libxrpl/protocol/STNumber.cpp index cdee274f67..8899a76a35 100644 --- a/src/libxrpl/protocol/STNumber.cpp +++ b/src/libxrpl/protocol/STNumber.cpp @@ -177,10 +177,10 @@ partsFromString(std::string const& number) // 6 = exponent sign // 7 = exponent number - bool negative = (match[1].matched && (match[1] == "-")); + bool const negative = (match[1].matched && (match[1] == "-")); - std::uint64_t mantissa; - int exponent; + std::uint64_t mantissa = 0; + int exponent = 0; if (!match[4].matched) // integer only { @@ -198,9 +198,13 @@ partsFromString(std::string const& number) { // we have an exponent if (match[6].matched && (match[6] == "-")) + { exponent -= boost::lexical_cast(std::string(match[7])); + } else + { exponent += boost::lexical_cast(std::string(match[7])); + } } return {mantissa, exponent, negative}; @@ -237,6 +241,7 @@ numberFromJson(SField const& field, Json::Value const& value) // Number mantissas are much bigger than the allowable parsed values, so // it can't be out of range. static_assert( + // NOLINTNEXTLINE(misc-redundant-expression) std::numeric_limits::max() >= std::numeric_limits::max()); } diff --git a/src/libxrpl/protocol/STObject.cpp b/src/libxrpl/protocol/STObject.cpp index 829f8eaeef..f758760337 100644 --- a/src/libxrpl/protocol/STObject.cpp +++ b/src/libxrpl/protocol/STObject.cpp @@ -46,7 +46,7 @@ STObject::STObject(STObject&& other) { } -STObject::STObject(SField const& name) : STBase(name), mType(nullptr) +STObject::STObject(SField const& name) : STBase(name) { } @@ -62,8 +62,7 @@ STObject::STObject(SOTemplate const& type, SerialIter& sit, SField const& name) applyTemplate(type); // May throw } -STObject::STObject(SerialIter& sit, SField const& name, int depth) noexcept(false) - : STBase(name), mType(nullptr) +STObject::STObject(SerialIter& sit, SField const& name, int depth) noexcept(false) : STBase(name) { if (depth > 10) Throw("Maximum nesting depth of STObject exceeded"); @@ -141,9 +140,13 @@ STObject::set(SOTemplate const& type) for (auto const& elem : type) { if (elem.style() != soeREQUIRED) + { v_.emplace_back(detail::nonPresentObject, elem.sField()); + } else + { v_.emplace_back(detail::defaultObject, elem.sField()); + } } } @@ -153,7 +156,7 @@ STObject::applyTemplate(SOTemplate const& type) auto throwFieldErr = [](std::string const& field, char const* description) { std::stringstream ss; ss << "Field '" << field << "' " << description; - std::string text{ss.str()}; + std::string const text{ss.str()}; JLOG(debugLog().error()) << "STObject::applyTemplate failed: " << text; Throw(text); }; @@ -201,7 +204,7 @@ void STObject::applyTemplateFromSField(SField const& sField) { SOTemplate const* elements = InnerObjectFormats::getInstance().findSOTemplateBySField(sField); - if (elements) + if (elements != nullptr) applyTemplate(*elements); // May throw } @@ -216,8 +219,8 @@ STObject::set(SerialIter& sit, int depth) // Consume data in the pipe until we run out or reach the end while (!sit.empty()) { - int type; - int field; + int type = 0; + int field = 0; // Get the metadata for the next field sit.getFieldID(type, field); @@ -269,11 +272,11 @@ STObject::set(SerialIter& sit, int depth) } bool -STObject::hasMatchingEntry(STBase const& t) +STObject::hasMatchingEntry(STBase const& t) const { STBase const* o = peekAtPField(t.getFName()); - if (!o) + if (o == nullptr) return false; return t == *o; @@ -291,16 +294,22 @@ STObject::getFullText() const ret += " = {"; } else + { ret = "{"; + } for (auto const& elem : v_) { if (elem->getSType() != STI_NOTPRESENT) { if (!first) + { ret += ", "; + } else + { first = false; + } ret += elem->getFullText(); } @@ -334,7 +343,7 @@ STObject::isEquivalent(STBase const& t) const { STObject const* v = dynamic_cast(&t); - if (!v) + if (v == nullptr) return false; if (mType != nullptr && v->mType == mType) @@ -391,7 +400,7 @@ STObject::getFieldIndex(SField const& field) const STBase const& STObject::peekAtField(SField const& field) const { - int index = getFieldIndex(field); + int const index = getFieldIndex(field); if (index == -1) throwFieldNotFound(field); @@ -402,7 +411,7 @@ STObject::peekAtField(SField const& field) const STBase& STObject::getField(SField const& field) { - int index = getFieldIndex(field); + int const index = getFieldIndex(field); if (index == -1) throwFieldNotFound(field); @@ -419,7 +428,7 @@ STObject::getFieldSType(int index) const STBase const* STObject::peekAtPField(SField const& field) const { - int index = getFieldIndex(field); + int const index = getFieldIndex(field); if (index == -1) return nullptr; @@ -430,7 +439,7 @@ STObject::peekAtPField(SField const& field) const STBase* STObject::getPField(SField const& field, bool createOkay) { - int index = getFieldIndex(field); + int const index = getFieldIndex(field); if (index == -1) { @@ -446,7 +455,7 @@ STObject::getPField(SField const& field, bool createOkay) bool STObject::isFieldPresent(SField const& field) const { - int index = getFieldIndex(field); + int const index = getFieldIndex(field); if (index == -1) return false; @@ -471,7 +480,7 @@ STObject::setFlag(std::uint32_t f) { STUInt32* t = dynamic_cast(getPField(sfFlags, true)); - if (!t) + if (t == nullptr) return false; t->setValue(t->value() | f); @@ -483,7 +492,7 @@ STObject::clearFlag(std::uint32_t f) { STUInt32* t = dynamic_cast(getPField(sfFlags)); - if (!t) + if (t == nullptr) return false; t->setValue(t->value() & ~f); @@ -501,7 +510,7 @@ STObject::getFlags(void) const { STUInt32 const* t = dynamic_cast(peekAtPField(sfFlags)); - if (!t) + if (t == nullptr) return 0; return t->value(); @@ -510,7 +519,7 @@ STObject::getFlags(void) const STBase* STObject::makeFieldPresent(SField const& field) { - int index = getFieldIndex(field); + int const index = getFieldIndex(field); if (index == -1) { @@ -520,7 +529,7 @@ STObject::makeFieldPresent(SField const& field) return getPIndex(emplace_back(detail::nonPresentObject, field)); } - STBase* f = getPIndex(index); + STBase* f = getPIndex(index); // NOLINT(misc-const-correctness) if (f->getSType() != STI_NOTPRESENT) return f; @@ -532,7 +541,7 @@ STObject::makeFieldPresent(SField const& field) void STObject::makeFieldAbsent(SField const& field) { - int index = getFieldIndex(field); + int const index = getFieldIndex(field); if (index == -1) throwFieldNotFound(field); @@ -547,7 +556,7 @@ STObject::makeFieldAbsent(SField const& field) bool STObject::delField(SField const& field) { - int index = getFieldIndex(field); + int const index = getFieldIndex(field); if (index == -1) return false; @@ -565,7 +574,7 @@ STObject::delField(int index) SOEStyle STObject::getStyle(SField const& field) const { - return mType ? mType->style(field) : soeINVALID; + return (mType != nullptr) ? mType->style(field) : soeINVALID; } unsigned char @@ -631,7 +640,7 @@ STObject::getAccountID(SField const& field) const Blob STObject::getFieldVL(SField const& field) const { - STBlob empty; + STBlob const empty; STBlob const& b = getFieldByConstRef(field, empty); return Blob(b.data(), b.data() + b.size()); } @@ -868,10 +877,7 @@ STObject::operator==(STObject const& obj) const ++fields; } - if (fields != matches) - return false; - - return true; + return fields == matches; } void @@ -908,7 +914,8 @@ STObject::getSortedFields(STObject const& objToSort, WhichFields whichFields) for (detail::STVar const& elem : objToSort.v_) { STBase const& base = elem.get(); - if ((base.getSType() != STI_NOTPRESENT) && base.getFName().shouldInclude(whichFields)) + if ((base.getSType() != STI_NOTPRESENT) && + base.getFName().shouldInclude(static_cast(whichFields))) { sf.push_back(&base); } diff --git a/src/libxrpl/protocol/STParsedJSON.cpp b/src/libxrpl/protocol/STParsedJSON.cpp index d72db32282..c928f49375 100644 --- a/src/libxrpl/protocol/STParsedJSON.cpp +++ b/src/libxrpl/protocol/STParsedJSON.cpp @@ -267,8 +267,10 @@ parseUint16( } } if (!ret) + { return parseUnsigned( field, json_name, fieldName, name, value, error); + } } catch (std::exception const&) { @@ -320,8 +322,10 @@ parseUint32( } } if (!ret) + { return parseUnsigned( field, json_name, fieldName, name, value, error); + } } catch (std::exception const&) { @@ -449,7 +453,7 @@ parseLeaf( { auto const str = value.asString(); - std::uint64_t val; + std::uint64_t val = 0; bool const useBase10 = field.shouldMeta(SField::sMD_BaseTen); @@ -1071,25 +1075,25 @@ parseArray( // TODO: There doesn't seem to be a nice way to get just the // first/only key in an object without copying all keys into // a vector - std::string const objectName(json[i].getMemberNames()[0]); + std::string const memberName(json[i].getMemberNames()[0]); ; - auto const& nameField(SField::getField(objectName)); + auto const& nameField(SField::getField(memberName)); if (nameField == sfInvalid) { - error = unknown_field(json_name, objectName); + error = unknown_field(json_name, memberName); return std::nullopt; } - Json::Value const objectFields(json[i][objectName]); + Json::Value const objectFields(json[i][memberName]); std::stringstream ss; - ss << json_name << "." << "[" << i << "]." << objectName; + ss << json_name << "." << "[" << i << "]." << memberName; auto ret = parseObject(ss.str(), objectFields, nameField, depth + 1, error); if (!ret) { - std::string errMsg = error["error_message"].asString(); + std::string const errMsg = error["error_message"].asString(); error["error_message"] = "Error at '" + ss.str() + "'. " + errMsg; return std::nullopt; } diff --git a/src/libxrpl/protocol/STPathSet.cpp b/src/libxrpl/protocol/STPathSet.cpp index 060f5c4d7c..2536c18e4e 100644 --- a/src/libxrpl/protocol/STPathSet.cpp +++ b/src/libxrpl/protocol/STPathSet.cpp @@ -45,7 +45,7 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name) std::vector path; for (;;) { - int iType = sit.get8(); + int const iType = sit.get8(); if (iType == STPathElement::typeNone || iType == STPathElement::typeBoundary) { @@ -61,7 +61,7 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name) if (iType == STPathElement::typeNone) return; } - else if (iType & ~STPathElement::typeAll) + else if ((iType & ~STPathElement::typeAll) != 0) { JLOG(debugLog().error()) << "Bad path element " << iType << " in pathset"; Throw("bad path element"); @@ -76,13 +76,13 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name) Currency currency; AccountID issuer; - if (hasAccount) + if (hasAccount != 0) account = sit.get160(); - if (hasCurrency) + if (hasCurrency != 0) currency = sit.get160(); - if (hasIssuer) + if (hasIssuer != 0) issuer = sit.get160(); path.emplace_back(account, currency, issuer, hasCurrency); @@ -127,7 +127,7 @@ bool STPathSet::isEquivalent(STBase const& t) const { STPathSet const* v = dynamic_cast(&t); - return v && (value == v->value); + return (v != nullptr) && (value == v->value); } bool @@ -153,20 +153,20 @@ STPath::getJson(JsonOptions) const { Json::Value ret(Json::arrayValue); - for (auto it : mPath) + for (auto const& it : mPath) { Json::Value elem(Json::objectValue); auto const iType = it.getNodeType(); elem[jss::type] = iType; - if (iType & STPathElement::typeAccount) + if ((iType & STPathElement::typeAccount) != 0u) elem[jss::account] = to_string(it.getAccountID()); - if (iType & STPathElement::typeCurrency) + if ((iType & STPathElement::typeCurrency) != 0u) elem[jss::currency] = to_string(it.getCurrency()); - if (iType & STPathElement::typeIssuer) + if ((iType & STPathElement::typeIssuer) != 0u) elem[jss::issuer] = to_string(it.getIssuerID()); ret.append(elem); @@ -179,7 +179,7 @@ Json::Value STPathSet::getJson(JsonOptions options) const { Json::Value ret(Json::arrayValue); - for (auto it : value) + for (auto const& it : value) ret.append(it.getJson(options)); return ret; @@ -205,17 +205,17 @@ STPathSet::add(Serializer& s) const for (auto const& speElement : spPath) { - int iType = speElement.getNodeType(); + int const iType = speElement.getNodeType(); s.add8(iType); - if (iType & STPathElement::typeAccount) + if ((iType & STPathElement::typeAccount) != 0) s.addBitString(speElement.getAccountID()); - if (iType & STPathElement::typeCurrency) + if ((iType & STPathElement::typeCurrency) != 0) s.addBitString(speElement.getCurrency()); - if (iType & STPathElement::typeIssuer) + if ((iType & STPathElement::typeIssuer) != 0) s.addBitString(speElement.getIssuerID()); } diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index 098ca1a400..f8600e167f 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -77,7 +77,7 @@ STTx::STTx(STObject&& object) : STObject(std::move(object)) STTx::STTx(SerialIter& sit) : STObject(sfTransaction) { - int length = sit.getBytesLeft(); + int const length = sit.getBytesLeft(); if ((length < txMinSizeBytes) || (length > txMaxSizeBytes)) Throw("Transaction length invalid"); @@ -199,8 +199,10 @@ STTx::getSeqProxy() const std::optional const ticketSeq{operator[](~sfTicketSequence)}; if (!ticketSeq) + { // No TicketSequence specified. Return the Sequence, whatever it is. return SeqProxy::sequence(seq); + } return SeqProxy{SeqProxy::ticket, *ticketSeq}; } @@ -211,6 +213,20 @@ STTx::getSeqValue() const return getSeqProxy().value(); } +AccountID +STTx::getFeePayer() const +{ + // If sfDelegate is present, the delegate account is the payer + // note: if a delegate is specified, its authorization to act on behalf of the account is + // enforced in `Transactor::checkPermission` + // cryptographic signature validity is checked separately (e.g., in `Transactor::checkSign`) + if (isFieldPresent(sfDelegate)) + return getAccountID(sfDelegate); + + // Default payer + return getAccountID(sfAccount); +} + void STTx::sign( PublicKey const& publicKey, @@ -313,7 +329,7 @@ STTx::getJson(JsonOptions options, bool binary) const if (binary) { - Serializer s = STObject::getSerializer(); + Serializer const s = STObject::getSerializer(); std::string const dataBin = strHex(s.peekData()); if (V1) @@ -323,8 +339,8 @@ STTx::getJson(JsonOptions options, bool binary) const ret[jss::hash] = to_string(getTransactionID()); return ret; } - else - return Json::Value{dataBin}; + + return Json::Value{dataBin}; } Json::Value ret = STObject::getJson(JsonOptions::none); @@ -351,7 +367,7 @@ STTx::getMetaSQL(std::uint32_t inLedger, std::string const& escapedMetaData) con { Serializer s; add(s); - return getMetaSQL(s, inLedger, txnSqlValidated, escapedMetaData); + return getMetaSQL(s, inLedger, TxnSql::txnSqlValidated, escapedMetaData); } // VFALCO This could be a free function elsewhere @@ -359,10 +375,10 @@ std::string STTx::getMetaSQL( Serializer rawTxn, std::uint32_t inLedger, - char status, + TxnSql status, std::string const& escapedMetaData) const { - static boost::format bfTrans("('%s', '%s', '%s', '%d', '%d', '%c', %s, %s)"); + static boost::format const bfTrans("('%s', '%s', '%s', '%d', '%d', '%c', %s, %s)"); std::string rTxn = sqlBlobLiteral(rawTxn.peekData()); auto format = TxFormats::getInstance().findByType(tx_type_); @@ -370,8 +386,8 @@ STTx::getMetaSQL( return str( boost::format(bfTrans) % to_string(getTransactionID()) % format->getName() % - toBase58(getAccountID(sfAccount)) % getFieldU32(sfSequence) % inLedger % status % rTxn % - escapedMetaData); + toBase58(getAccountID(sfAccount)) % getFieldU32(sfSequence) % inLedger % + safe_cast(status) % rTxn % escapedMetaData); } static Expected @@ -486,9 +502,11 @@ multiSignHelper( errorWhat = e.what(); } if (!validSig) + { return Unexpected( std::string("Invalid signature on account ") + toBase58(accountID) + errorWhat.value_or("") + "."); + } } // All signatures verified. return {}; @@ -556,13 +574,13 @@ STTx::getBatchTransactionIDs() const { XRPL_ASSERT(getTxnType() == ttBATCH, "STTx::getBatchTransactionIDs : not a batch transaction"); XRPL_ASSERT( - getFieldArray(sfRawTransactions).size() != 0, + !getFieldArray(sfRawTransactions).empty(), "STTx::getBatchTransactionIDs : empty raw transactions"); // The list of inner ids is built once, then reused on subsequent calls. // After the list is built, it must always have the same size as the array // `sfRawTransactions`. The assert below verifies that. - if (batchTxnIds_.size() == 0) + if (batchTxnIds_.empty()) { for (STObject const& rb : getFieldArray(sfRawTransactions)) batchTxnIds_.push_back(rb.getHash(HashPrefix::transactionID)); @@ -600,7 +618,7 @@ isMemoOkay(STObject const& st, std::string& reason) { auto memoObj = dynamic_cast(&memo); - if (!memoObj || (memoObj->getFName() != sfMemo)) + if ((memoObj == nullptr) || (memoObj->getFName() != sfMemo)) { reason = "A memo array may contain only Memo objects."; return false; @@ -638,20 +656,20 @@ isMemoOkay(STObject const& st, std::string& reason) static constexpr std::array const allowedSymbols = []() { std::array a{}; - std::string_view symbols( + std::string_view const symbols( "0123456789" "-._~:/?#[]@!$&'()*+,;=%" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"); - for (unsigned char c : symbols) + for (unsigned char const c : symbols) a[c] = 1; return a; }(); - for (unsigned char c : *optData) + for (unsigned char const c : *optData) { - if (!allowedSymbols[c]) + if (allowedSymbols[c] == 0) { reason = "The MemoType and MemoFormat fields may only " @@ -673,7 +691,7 @@ isAccountFieldOkay(STObject const& st) for (int i = 0; i < st.getCount(); ++i) { auto t = dynamic_cast(st.peekAtPIndex(i)); - if (t && t->isDefault()) + if ((t != nullptr) && t->isDefault()) return false; } @@ -694,9 +712,9 @@ invalidMPTAmountInTx(STObject const& tx) { if (auto const& field = tx.peekAtField(e.sField()); (field.getSType() == STI_AMOUNT && - static_cast(field).holds()) || + safe_downcast(field).holds()) || (field.getSType() == STI_ISSUE && - static_cast(field).holds())) + safe_downcast(field).holds())) { if (e.supportMPT() != soeMPTSupported) return true; diff --git a/src/libxrpl/protocol/STValidation.cpp b/src/libxrpl/protocol/STValidation.cpp index f6f89d43e9..f4042844f4 100644 --- a/src/libxrpl/protocol/STValidation.cpp +++ b/src/libxrpl/protocol/STValidation.cpp @@ -38,22 +38,22 @@ STValidation::validationFormat() // guarantee the initialization order. // clang-format off static SOTemplate const format{ - {sfFlags, soeREQUIRED}, - {sfLedgerHash, soeREQUIRED}, - {sfLedgerSequence, soeREQUIRED}, - {sfCloseTime, soeOPTIONAL}, - {sfLoadFee, soeOPTIONAL}, - {sfAmendments, soeOPTIONAL}, - {sfBaseFee, soeOPTIONAL}, - {sfReserveBase, soeOPTIONAL}, - {sfReserveIncrement, soeOPTIONAL}, - {sfSigningTime, soeREQUIRED}, - {sfSigningPubKey, soeREQUIRED}, - {sfSignature, soeREQUIRED}, - {sfConsensusHash, soeOPTIONAL}, - {sfCookie, soeDEFAULT}, - {sfValidatedHash, soeOPTIONAL}, - {sfServerVersion, soeOPTIONAL}, + {sfFlags, soeREQUIRED}, + {sfLedgerHash, soeREQUIRED}, + {sfLedgerSequence, soeREQUIRED}, + {sfCloseTime, soeOPTIONAL}, + {sfLoadFee, soeOPTIONAL}, + {sfAmendments, soeOPTIONAL}, + {sfBaseFee, soeOPTIONAL}, + {sfReserveBase, soeOPTIONAL}, + {sfReserveIncrement, soeOPTIONAL}, + {sfSigningTime, soeREQUIRED}, + {sfSigningPubKey, soeREQUIRED}, + {sfSignature, soeREQUIRED}, + {sfConsensusHash, soeOPTIONAL}, + {sfCookie, soeDEFAULT}, + {sfValidatedHash, soeOPTIONAL}, + {sfServerVersion, soeOPTIONAL}, // featureXRPFees {sfBaseFeeDrops, soeOPTIONAL}, {sfReserveBaseDrops, soeOPTIONAL}, @@ -107,7 +107,7 @@ STValidation::isValid() const noexcept getSignerPublic(), getSigningHash(), makeSlice(getFieldVL(sfSignature)), - getFlags() & vfFullyCanonicalSig); + (getFlags() & vfFullyCanonicalSig) != 0u); } return valid_.value(); diff --git a/src/libxrpl/protocol/STVar.cpp b/src/libxrpl/protocol/STVar.cpp index 6218bb6db6..8b76d8a322 100644 --- a/src/libxrpl/protocol/STVar.cpp +++ b/src/libxrpl/protocol/STVar.cpp @@ -60,10 +60,14 @@ STVar::operator=(STVar const& rhs) if (&rhs != this) { destroy(); - if (rhs.p_) + if (rhs.p_ != nullptr) + { p_ = rhs.p_->copy(max_size, &d_); + } else + { p_ = nullptr; + } } return *this; @@ -116,9 +120,13 @@ void STVar::destroy() { if (on_heap()) + { delete p_; + } else + { p_->~STBase(); + } p_ = nullptr; } diff --git a/src/libxrpl/protocol/STVector256.cpp b/src/libxrpl/protocol/STVector256.cpp index 886fed5d2d..2a70dd1e05 100644 --- a/src/libxrpl/protocol/STVector256.cpp +++ b/src/libxrpl/protocol/STVector256.cpp @@ -19,8 +19,10 @@ STVector256::STVector256(SerialIter& sit, SField const& name) : STBase(name) auto const slice = sit.getSlice(sit.getVLDataLength()); if (slice.size() % uint256::size() != 0) + { Throw( "Bad serialization for STVector256: " + std::to_string(slice.size())); + } auto const cnt = slice.size() / uint256::size(); @@ -66,7 +68,7 @@ bool STVector256::isEquivalent(STBase const& t) const { STVector256 const* v = dynamic_cast(&t); - return v && (mValue == v->mValue); + return (v != nullptr) && (mValue == v->mValue); } Json::Value diff --git a/src/libxrpl/protocol/STXChainBridge.cpp b/src/libxrpl/protocol/STXChainBridge.cpp index 85d36f9b46..428e4655ff 100644 --- a/src/libxrpl/protocol/STXChainBridge.cpp +++ b/src/libxrpl/protocol/STXChainBridge.cpp @@ -167,7 +167,7 @@ bool STXChainBridge::isEquivalent(STBase const& t) const { STXChainBridge const* v = dynamic_cast(&t); - return v && (*v == *this); + return (v != nullptr) && (*v == *this); } bool diff --git a/src/libxrpl/protocol/SecretKey.cpp b/src/libxrpl/protocol/SecretKey.cpp index 4f0963e72d..f58bb81a88 100644 --- a/src/libxrpl/protocol/SecretKey.cpp +++ b/src/libxrpl/protocol/SecretKey.cpp @@ -74,7 +74,7 @@ deriveDeterministicRootKey(Seed const& seed) // buf |----------------|----| // | seed | seq| - std::array buf; + std::array buf{}; std::copy(seed.begin(), seed.end(), buf.begin()); // The odds that this loop executes more than once are negligible @@ -119,7 +119,7 @@ class Generator { private: uint256 root_; - std::array generator_; + std::array generator_{}; uint256 calculateTweak(std::uint32_t seq) const @@ -133,7 +133,7 @@ private: // buf |---------------------------------|----|----| // | generator | seq| cnt| - std::array buf; + std::array buf{}; std::copy(generator_.begin(), generator_.end(), buf.begin()); copy_uint32(buf.data() + 33, seq); @@ -185,7 +185,7 @@ public: if (secp256k1_ec_seckey_tweak_add(secp256k1Context(), rpk.data(), tweak.data()) == 1) { - SecretKey sk{Slice{rpk.data(), rpk.size()}}; + SecretKey const sk{Slice{rpk.data(), rpk.size()}}; secure_erase(rpk.data(), rpk.size()); return sk; } @@ -270,7 +270,7 @@ randomSecretKey() { std::uint8_t buf[32]; beast::rngfill(buf, sizeof(buf), crypto_prng()); - SecretKey sk(Slice{buf, sizeof(buf)}); + SecretKey const sk(Slice{buf, sizeof(buf)}); secure_erase(buf, sizeof(buf)); return sk; } @@ -281,7 +281,7 @@ generateSecretKey(KeyType type, Seed const& seed) if (type == KeyType::ed25519) { auto key = sha512Half_s(Slice(seed.data(), seed.size())); - SecretKey sk{Slice{key.data(), key.size()}}; + SecretKey const sk{Slice{key.data(), key.size()}}; secure_erase(key.data(), key.size()); return sk; } @@ -289,7 +289,7 @@ generateSecretKey(KeyType type, Seed const& seed) if (type == KeyType::secp256k1) { auto key = detail::deriveDeterministicRootKey(seed); - SecretKey sk{Slice{key.data(), key.size()}}; + SecretKey const sk{Slice{key.data(), key.size()}}; secure_erase(key.data(), key.size()); return sk; } @@ -335,7 +335,7 @@ generateKeyPair(KeyType type, Seed const& seed) switch (type) { case KeyType::secp256k1: { - detail::Generator g(seed); + detail::Generator const g(seed); return g(0); } default: diff --git a/src/libxrpl/protocol/Seed.cpp b/src/libxrpl/protocol/Seed.cpp index 0e0bb7fb2b..6d33013f2f 100644 --- a/src/libxrpl/protocol/Seed.cpp +++ b/src/libxrpl/protocol/Seed.cpp @@ -46,9 +46,9 @@ Seed::Seed(uint128 const& seed) Seed randomSeed() { - std::array buffer; + std::array buffer{}; beast::rngfill(buffer.data(), buffer.size(), crypto_prng()); - Seed seed(makeSlice(buffer)); + Seed const seed(makeSlice(buffer)); secure_erase(buffer.data(), buffer.size()); return seed; } diff --git a/src/libxrpl/protocol/Serializer.cpp b/src/libxrpl/protocol/Serializer.cpp index 483e6b516f..4e6a49f572 100644 --- a/src/libxrpl/protocol/Serializer.cpp +++ b/src/libxrpl/protocol/Serializer.cpp @@ -23,7 +23,7 @@ namespace xrpl { int Serializer::add16(std::uint16_t i) { - int ret = mData.size(); + int const ret = mData.size(); mData.push_back(static_cast(i >> 8)); mData.push_back(static_cast(i & 0xff)); return ret; @@ -73,7 +73,7 @@ Serializer::addInteger(std::int32_t i) int Serializer::addRaw(Blob const& vector) { - int ret = mData.size(); + int const ret = mData.size(); mData.insert(mData.end(), vector.begin(), vector.end()); return ret; } @@ -81,7 +81,7 @@ Serializer::addRaw(Blob const& vector) int Serializer::addRaw(Slice slice) { - int ret = mData.size(); + int const ret = mData.size(); mData.insert(mData.end(), slice.begin(), slice.end()); return ret; } @@ -89,7 +89,7 @@ Serializer::addRaw(Slice slice) int Serializer::addRaw(Serializer const& s) { - int ret = mData.size(); + int const ret = mData.size(); mData.insert(mData.end(), s.begin(), s.end()); return ret; } @@ -97,7 +97,7 @@ Serializer::addRaw(Serializer const& s) int Serializer::addRaw(void const* ptr, int len) { - int ret = mData.size(); + int const ret = mData.size(); mData.insert(mData.end(), (char const*)ptr, ((char const*)ptr) + len); return ret; } @@ -105,15 +105,17 @@ Serializer::addRaw(void const* ptr, int len) int Serializer::addFieldID(int type, int name) { - int ret = mData.size(); + int const ret = mData.size(); XRPL_ASSERT( (type > 0) && (type < 256) && (name > 0) && (name < 256), "xrpl::Serializer::addFieldID : inputs inside range"); if (type < 16) { - if (name < 16) // common type, common name + if (name < 16) + { // common type, common name mData.push_back(static_cast((type << 4) | name)); + } else { // common type, uncommon name @@ -141,7 +143,7 @@ Serializer::addFieldID(int type, int name) int Serializer::add8(unsigned char byte) { - int ret = mData.size(); + int const ret = mData.size(); mData.push_back(byte); return ret; } @@ -175,7 +177,7 @@ Serializer::getSHA512Half() const int Serializer::addVL(Blob const& vector) { - int ret = addEncoded(vector.size()); + int const ret = addEncoded(vector.size()); addRaw(vector); XRPL_ASSERT( mData.size() == (ret + vector.size() + encodeLengthLength(vector.size())), @@ -186,8 +188,8 @@ Serializer::addVL(Blob const& vector) int Serializer::addVL(Slice const& slice) { - int ret = addEncoded(slice.size()); - if (slice.size()) + int const ret = addEncoded(slice.size()); + if (!slice.empty()) addRaw(slice.data(), slice.size()); return ret; } @@ -195,9 +197,9 @@ Serializer::addVL(Slice const& slice) int Serializer::addVL(void const* ptr, int len) { - int ret = addEncoded(len); + int const ret = addEncoded(len); - if (len) + if (len != 0) addRaw(ptr, len); return ret; @@ -206,7 +208,7 @@ Serializer::addVL(void const* ptr, int len) int Serializer::addEncoded(int length) { - std::array bytes; + std::array bytes{}; int numBytes = 0; if (length <= 192) @@ -230,7 +232,9 @@ Serializer::addEncoded(int length) numBytes = 3; } else + { Throw("lenlen"); + } return addRaw(&bytes[0], numBytes); } @@ -294,7 +298,7 @@ Serializer::decodeVLLength(int b1, int b2) if (b1 > 240) Throw("b1>240"); - return 193 + (b1 - 193) * 256 + b2; + return 193 + ((b1 - 193) * 256) + b2; } int @@ -306,7 +310,7 @@ Serializer::decodeVLLength(int b1, int b2, int b3) if (b1 > 254) Throw("b1>254"); - return 12481 + (b1 - 241) * 65536 + b2 * 256 + b3; + return 12481 + ((b1 - 241) * 65536) + (b2 * 256) + b3; } //------------------------------------------------------------------------------ @@ -339,7 +343,7 @@ SerialIter::get8() { if (remain_ < 1) Throw("invalid SerialIter get8"); - unsigned char t = *p_; + unsigned char const t = *p_; ++p_; ++used_; --remain_; @@ -465,23 +469,23 @@ SerialIter::getRaw(int size) int SerialIter::getVLDataLength() { - int b1 = get8(); - int datLen; - int lenLen = Serializer::decodeLengthLength(b1); + int const b1 = get8(); + int datLen = 0; + int const lenLen = Serializer::decodeLengthLength(b1); if (lenLen == 1) { datLen = Serializer::decodeVLLength(b1); } else if (lenLen == 2) { - int b2 = get8(); + int const b2 = get8(); datLen = Serializer::decodeVLLength(b1, b2); } else { XRPL_ASSERT(lenLen == 3, "xrpl::SerialIter::getVLDataLength : lenLen is 3"); - int b2 = get8(); - int b3 = get8(); + int const b2 = get8(); + int const b3 = get8(); datLen = Serializer::decodeVLLength(b1, b2, b3); } return datLen; @@ -492,7 +496,7 @@ SerialIter::getSlice(std::size_t bytes) { if (bytes > remain_) Throw("invalid SerialIter getSlice"); - Slice s(p_, bytes); + Slice const s(p_, bytes); p_ += bytes; used_ += bytes; remain_ -= bytes; diff --git a/src/libxrpl/protocol/TxMeta.cpp b/src/libxrpl/protocol/TxMeta.cpp index f4932b2af4..3c797a7c8c 100644 --- a/src/libxrpl/protocol/TxMeta.cpp +++ b/src/libxrpl/protocol/TxMeta.cpp @@ -28,7 +28,7 @@ TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, STObject const& obj) auto affectedNodes = dynamic_cast(obj.peekAtPField(sfAffectedNodes)); XRPL_ASSERT(affectedNodes, "xrpl::TxMeta::TxMeta(STObject) : type cast succeeded"); - if (affectedNodes) + if (affectedNodes != nullptr) nodes_ = *affectedNodes; setAdditionalFields(obj); @@ -39,7 +39,7 @@ TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, Blob const& vec) { SerialIter sit(makeSlice(vec)); - STObject obj(sit, sfMetadata); + STObject const obj(sit, sfMetadata); result_ = obj.getFieldU8(sfTransactionResult); index_ = obj.getFieldU32(sfTransactionIndex); nodes_ = obj.getFieldArray(sfAffectedNodes); @@ -89,14 +89,14 @@ TxMeta::getAffectedAccounts() const // Meta#getAffectedAccounts for (auto const& node : nodes_) { - int index = + int const index = node.getFieldIndex((node.getFName() == sfCreatedNode) ? sfNewFields : sfFinalFields); if (index != -1) { auto const* inner = dynamic_cast(&node.peekAtIndex(index)); XRPL_ASSERT(inner, "xrpl::getAffectedAccounts : STObject type cast succeeded"); - if (inner) + if (inner != nullptr) { for (auto const& field : *inner) { @@ -146,7 +146,7 @@ TxMeta::getAffectedAccounts() const STObject& TxMeta::getAffectedNode(SLE::ref node, SField const& type) { - uint256 index = node->key(); + uint256 const index = node->key(); for (auto& n : nodes_) { if (n.getFieldH256(sfLedgerIndex) == index) diff --git a/src/libxrpl/protocol/UintTypes.cpp b/src/libxrpl/protocol/UintTypes.cpp index df9eadb511..5ace6c29d6 100644 --- a/src/libxrpl/protocol/UintTypes.cpp +++ b/src/libxrpl/protocol/UintTypes.cpp @@ -63,7 +63,7 @@ to_string(Currency const& currency) bool to_currency(Currency& currency, std::string const& code) { - if (code.empty() || !code.compare(systemCurrencyCode())) + if (code.empty() || (code.compare(systemCurrencyCode()) == 0)) { currency = beast::zero; return true; diff --git a/src/libxrpl/protocol/XChainAttestations.cpp b/src/libxrpl/protocol/XChainAttestations.cpp index 314bf9f6d3..c255f743e3 100644 --- a/src/libxrpl/protocol/XChainAttestations.cpp +++ b/src/libxrpl/protocol/XChainAttestations.cpp @@ -74,7 +74,7 @@ AttestationBase::sameEventHelper(AttestationBase const& lhs, AttestationBase con bool AttestationBase::verify(STXChainBridge const& bridge) const { - std::vector msg = message(bridge); + std::vector const msg = message(bridge); return xrpl::verify(publicKey, makeSlice(msg), signature); } diff --git a/src/libxrpl/protocol/tokens.cpp b/src/libxrpl/protocol/tokens.cpp index 867c69a802..e2a77fbf10 100644 --- a/src/libxrpl/protocol/tokens.cpp +++ b/src/libxrpl/protocol/tokens.cpp @@ -263,7 +263,7 @@ decodeBase58(std::string const& s) // Allocate enough space in big-endian base256 representation. // log(58) / log(256), rounded up. - std::vector b256(remain * 733 / 1000 + 1); + std::vector b256((remain * 733 / 1000) + 1); while (remain > 0) { auto carry = alphabetReverse[*psz]; @@ -308,7 +308,7 @@ encodeBase58Token(TokenType type, void const* token, std::size_t size) // Lay the data out as // buf[0] = safe_cast>(type); - if (size) + if (size != 0u) std::memcpy(buf.data() + 1, token, size); checksum(buf.data() + 1 + size, buf.data(), 1 + size); @@ -329,7 +329,7 @@ decodeBase58Token(std::string const& s, TokenType type) return {}; // And the checksum must as well. - std::array guard; + std::array guard{}; checksum(guard.data(), ret.data(), ret.size() - guard.size()); if (!std::equal(guard.rbegin(), guard.rend(), ret.rbegin())) return {}; @@ -383,7 +383,7 @@ b256_to_b58_be(std::span input, std::span out) { break; } - auto const src_i_end = input.size() - i * 8; + auto const src_i_end = input.size() - (i * 8); if (src_i_end >= 8) { std::memcpy(&base_2_64_coeff_buf[num_coeff], &input[src_i_end - 8], 8); @@ -445,12 +445,12 @@ b256_to_b58_be(std::span input, std::span out) std::array const b58_be = xrpl::b58_fast::detail::b58_10_to_b58_be(base_58_10_coeff[i]); std::size_t to_skip = 0; - std::span b58_be_s{b58_be.data(), b58_be.size()}; + std::span const b58_be_s{b58_be.data(), b58_be.size()}; if (skip_zeros) { to_skip = count_leading_zeros(b58_be_s); skip_zeros = false; - if (out.size() < (i + 1) * 10 - to_skip) + if (out.size() < ((i + 1) * 10) - to_skip) { return Unexpected(TokenCodecErrc::outputTooSmall); } @@ -502,12 +502,12 @@ b58_to_b256_be(std::string_view input, std::span out) // log(2^(38*8),58^10)) ~= 5.18. So 6 coeff are enough std::array b_58_10_coeff{}; auto [num_full_coeffs, partial_coeff_len] = xrpl::b58_fast::detail::div_rem(input.size(), 10); - auto const num_partial_coeffs = partial_coeff_len ? 1 : 0; + auto const num_partial_coeffs = (partial_coeff_len != 0u) ? 1 : 0; auto const num_b_58_10_coeffs = num_full_coeffs + num_partial_coeffs; XRPL_ASSERT( num_b_58_10_coeffs <= b_58_10_coeff.size(), "xrpl::b58_fast::detail::b58_to_b256_be : maximum coeff"); - for (unsigned char c : input.substr(0, partial_coeff_len)) + for (unsigned char const c : input.substr(0, partial_coeff_len)) { auto cur_val = ::xrpl::alphabetReverse[c]; if (cur_val < 0) @@ -521,7 +521,7 @@ b58_to_b256_be(std::string_view input, std::span out) { for (int j = 0; j < num_full_coeffs; ++j) { - unsigned char c = input[partial_coeff_len + j * 10 + i]; + unsigned char const c = input[partial_coeff_len + (j * 10) + i]; auto cur_val = ::xrpl::alphabetReverse[c]; if (cur_val < 0) { @@ -586,7 +586,7 @@ b58_to_b256_be(std::string_view input, std::span out) cur_out_i += 1; } } - if ((cur_out_i + 8 * (cur_result_size - 1)) > out.size()) + if ((cur_out_i + (8 * (cur_result_size - 1))) > out.size()) { return Unexpected(TokenCodecErrc::outputTooSmall); } @@ -610,12 +610,12 @@ encodeBase58Token( std::span out) { constexpr std::size_t tmpBufSize = 128; - std::array buf; + std::array buf{}; if (input.size() > tmpBufSize - 5) { return Unexpected(TokenCodecErrc::inputTooLarge); } - if (input.size() == 0) + if (input.empty()) { return Unexpected(TokenCodecErrc::inputTooSmall); } @@ -626,7 +626,7 @@ encodeBase58Token( size_t const checksum_i = input.size() + 1; // buf[checksum_i..checksum_i + 4] = checksum checksum(buf.data() + checksum_i, buf.data(), checksum_i); - std::span b58Span(buf.data(), input.size() + 5); + std::span const b58Span(buf.data(), input.size() + 5); return detail::b256_to_b58_be(b58Span, out); } // Convert from base 58 to base 256, largest coefficients first @@ -637,7 +637,7 @@ encodeBase58Token( B58Result> decodeBase58Token(TokenType type, std::string_view s, std::span outBuf) { - std::array tmpBuf; + std::array tmpBuf{}; auto const decodeResult = detail::b58_to_b256_be(s, std::span(tmpBuf.data(), tmpBuf.size())); if (!decodeResult) @@ -654,7 +654,7 @@ decodeBase58Token(TokenType type, std::string_view s, std::span ou return Unexpected(TokenCodecErrc::mismatchedTokenType); // And the checksum must as well. - std::array guard; + std::array guard{}; checksum(guard.data(), ret.data(), ret.size() - guard.size()); if (!std::equal(guard.rbegin(), guard.rend(), ret.rbegin())) { @@ -680,8 +680,8 @@ encodeBase58Token(TokenType type, void const* token, std::size_t size) // over-allocation, this function uses 128 (again, over-allocation assuming // 2 base 58 char per byte) sr.resize(128); - std::span outSp(reinterpret_cast(sr.data()), sr.size()); - std::span inSp(reinterpret_cast(token), size); + std::span const outSp(reinterpret_cast(sr.data()), sr.size()); + std::span const inSp(reinterpret_cast(token), size); auto r = b58_fast::encodeBase58Token(type, inSp, outSp); if (!r) return {}; @@ -696,7 +696,7 @@ decodeBase58Token(std::string const& s, TokenType type) // The largest object encoded as base58 is 33 bytes; 64 is plenty (and // there's no benefit making it smaller) sr.resize(64); - std::span outSp(reinterpret_cast(sr.data()), sr.size()); + std::span const outSp(reinterpret_cast(sr.data()), sr.size()); auto r = b58_fast::decodeBase58Token(type, s, outSp); if (!r) return {}; diff --git a/src/libxrpl/protocol_autogen/placeholder.cpp b/src/libxrpl/protocol_autogen/placeholder.cpp new file mode 100644 index 0000000000..b48581e5b8 --- /dev/null +++ b/src/libxrpl/protocol_autogen/placeholder.cpp @@ -0,0 +1,5 @@ +// This file is a placeholder to ensure the protocol_autogen module can be built. +#include +#include +#include +#include diff --git a/src/libxrpl/rdb/DatabaseCon.cpp b/src/libxrpl/rdb/DatabaseCon.cpp index 344df85b4a..bca14f9bfa 100644 --- a/src/libxrpl/rdb/DatabaseCon.cpp +++ b/src/libxrpl/rdb/DatabaseCon.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include #include @@ -25,26 +25,29 @@ public: std::shared_ptr fromId(std::uintptr_t id) { - std::lock_guard l{mutex_}; + std::lock_guard const l{mutex_}; auto it = checkpointers_.find(id); if (it != checkpointers_.end()) return it->second; - return {}; + return nullptr; } void erase(std::uintptr_t id) { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; checkpointers_.erase(id); } std::shared_ptr - create(std::shared_ptr const& session, JobQueue& jobQueue, Logs& logs) + create( + std::shared_ptr const& session, + JobQueue& jobQueue, + ServiceRegistry& registry) { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; auto const id = nextId_++; - auto const r = makeCheckpointer(id, session, jobQueue, logs); + auto const r = makeCheckpointer(id, session, jobQueue, registry); checkpointers_[id] = r; return r; } @@ -64,7 +67,7 @@ DatabaseCon::~DatabaseCon() { checkpointers.erase(checkpointer_->id()); - std::weak_ptr wk(checkpointer_); + std::weak_ptr const wk(checkpointer_); checkpointer_.reset(); // The references to our Checkpointer held by 'checkpointer_' and @@ -72,7 +75,7 @@ DatabaseCon::~DatabaseCon() // checkpoint is currently in progress. Wait for it to end, otherwise // creating a new DatabaseCon to the same database may fail due to the // database being locked by our (now old) Checkpointer. - while (wk.use_count()) + while (wk.use_count() != 0) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } @@ -82,11 +85,11 @@ DatabaseCon::~DatabaseCon() std::unique_ptr const> DatabaseCon::Setup::globalPragma; void -DatabaseCon::setupCheckpointing(JobQueue* q, Logs& l) +DatabaseCon::setupCheckpointing(JobQueue* q, ServiceRegistry& registry) { - if (!q) + if (q == nullptr) Throw("No JobQueue"); - checkpointer_ = checkpointers.create(session_, *q, l); + checkpointer_ = checkpointers.create(session_, *q, registry); } } // namespace xrpl diff --git a/src/libxrpl/rdb/SociDB.cpp b/src/libxrpl/rdb/SociDB.cpp index 3ba0d4c980..780e05854f 100644 --- a/src/libxrpl/rdb/SociDB.cpp +++ b/src/libxrpl/rdb/SociDB.cpp @@ -81,20 +81,24 @@ void open(soci::session& s, std::string const& beName, std::string const& connectionString) { if (beName == "sqlite") + { s.open(soci::sqlite3, connectionString); + } else + { Throw("Unsupported soci backend: " + beName); + } } static sqlite_api::sqlite3* getConnection(soci::session& s) { - sqlite_api::sqlite3* result = nullptr; + sqlite_api::sqlite3* result = nullptr; // NOLINT(misc-const-correctness) auto be = s.get_backend(); if (auto b = dynamic_cast(be)) result = b->conn_; - if (!result) + if (result == nullptr) Throw("Didn't get a database connection."); return result; @@ -103,7 +107,7 @@ getConnection(soci::session& s) std::uint32_t getKBUsedAll(soci::session& s) { - if (!getConnection(s)) + if (getConnection(s) == nullptr) Throw("No connection found."); return static_cast(sqlite_api::sqlite3_memory_used() / kilobytes(1)); } @@ -143,18 +147,26 @@ void convert(std::vector const& from, soci::blob& to) { if (!from.empty()) + { to.write(0, reinterpret_cast(&from[0]), from.size()); + } else + { to.trim(0); + } } void convert(std::string const& from, soci::blob& to) { if (!from.empty()) + { to.write(0, from.data(), from.size()); + } else + { to.trim(0); + } } namespace { @@ -175,8 +187,11 @@ public: std::uintptr_t id, std::weak_ptr session, JobQueue& q, - Logs& logs) - : id_(id), session_(std::move(session)), jobQueue_(q), j_(logs.journal("WALCheckpointer")) + ServiceRegistry& registry) + : id_(id) + , session_(std::move(session)) + , jobQueue_(q) + , j_(registry.getJournal("WALCheckpointer")) { if (auto [conn, keepAlive] = getConnection(); conn) { @@ -207,7 +222,7 @@ public: schedule() override { { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); if (running_) return; running_ = true; @@ -227,7 +242,7 @@ public: self->checkpoint(); })) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); running_ = false; } } @@ -237,11 +252,12 @@ public: { auto [conn, keepAlive] = getConnection(); (void)keepAlive; - if (!conn) + if (conn == nullptr) return; int log = 0, ckpt = 0; - int ret = sqlite3_wal_checkpoint_v2(conn, nullptr, SQLITE_CHECKPOINT_PASSIVE, &log, &ckpt); + int const ret = + sqlite3_wal_checkpoint_v2(conn, nullptr, SQLITE_CHECKPOINT_PASSIVE, &log, &ckpt); auto fname = sqlite3_db_filename(conn, "main"); if (ret != SQLITE_OK) @@ -254,7 +270,7 @@ public: JLOG(j_.trace()) << "WAL(" << fname << "): frames=" << log << ", written=" << ckpt; } - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); running_ = false; } @@ -295,9 +311,9 @@ makeCheckpointer( std::uintptr_t id, std::weak_ptr session, JobQueue& queue, - Logs& logs) + ServiceRegistry& registry) { - return std::make_shared(id, std::move(session), queue, logs); + return std::make_shared(id, std::move(session), queue, registry); } } // namespace xrpl diff --git a/src/libxrpl/resource/Consumer.cpp b/src/libxrpl/resource/Consumer.cpp index 11c23b1044..c1cb9d4367 100644 --- a/src/libxrpl/resource/Consumer.cpp +++ b/src/libxrpl/resource/Consumer.cpp @@ -23,7 +23,7 @@ Consumer::Consumer() : m_logic(nullptr), m_entry(nullptr) Consumer::Consumer(Consumer const& other) : m_logic(other.m_logic), m_entry(nullptr) { - if (m_logic && other.m_entry) + if ((m_logic != nullptr) && (other.m_entry != nullptr)) { m_entry = other.m_entry; m_logic->acquire(*m_entry); @@ -32,22 +32,25 @@ Consumer::Consumer(Consumer const& other) : m_logic(other.m_logic), m_entry(null Consumer::~Consumer() { - if (m_logic && m_entry) + if ((m_logic != nullptr) && (m_entry != nullptr)) m_logic->release(*m_entry); } Consumer& Consumer::operator=(Consumer const& other) { + if (this == &other) + return *this; + // remove old ref - if (m_logic && m_entry) + if ((m_logic != nullptr) && (m_entry != nullptr)) m_logic->release(*m_entry); m_logic = other.m_logic; m_entry = other.m_entry; // add new ref - if (m_logic && m_entry) + if ((m_logic != nullptr) && (m_entry != nullptr)) m_logic->acquire(*m_entry); return *this; @@ -65,7 +68,7 @@ Consumer::to_string() const bool Consumer::isUnlimited() const { - if (m_entry) + if (m_entry != nullptr) return m_entry->isUnlimited(); return false; @@ -75,7 +78,7 @@ Disposition Consumer::disposition() const { Disposition d = ok; - if (m_logic && m_entry) + if ((m_logic != nullptr) && (m_entry != nullptr)) d = m_logic->charge(*m_entry, Charge(0)); return d; @@ -86,7 +89,7 @@ Consumer::charge(Charge const& what, std::string const& context) { Disposition d = ok; - if (m_logic && m_entry && !m_entry->isUnlimited()) + if ((m_logic != nullptr) && (m_entry != nullptr) && !m_entry->isUnlimited()) d = m_logic->charge(*m_entry, what, context); return d; diff --git a/src/libxrpl/resource/ResourceManager.cpp b/src/libxrpl/resource/ResourceManager.cpp index 1e7aadfa8d..d0e45d4c0e 100644 --- a/src/libxrpl/resource/ResourceManager.cpp +++ b/src/libxrpl/resource/ResourceManager.cpp @@ -50,7 +50,7 @@ public: ~ManagerImp() override { { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); stop_ = true; cond_.notify_one(); } diff --git a/src/libxrpl/server/InfoSub.cpp b/src/libxrpl/server/InfoSub.cpp index 0ec432883d..ea70dc3a83 100644 --- a/src/libxrpl/server/InfoSub.cpp +++ b/src/libxrpl/server/InfoSub.cpp @@ -52,7 +52,7 @@ InfoSub::getConsumer() } std::uint64_t -InfoSub::getSeq() +InfoSub::getSeq() const { return mSeq; } @@ -65,36 +65,44 @@ InfoSub::onSendEmpty() void InfoSub::insertSubAccountInfo(AccountID const& account, bool rt) { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); if (rt) + { realTimeSubscriptions_.insert(account); + } else + { normalSubscriptions_.insert(account); + } } void InfoSub::deleteSubAccountInfo(AccountID const& account, bool rt) { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); if (rt) + { realTimeSubscriptions_.erase(account); + } else + { normalSubscriptions_.erase(account); + } } bool InfoSub::insertSubAccountHistory(AccountID const& account) { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); return accountHistorySubscriptions_.insert(account).second; } void InfoSub::deleteSubAccountHistory(AccountID const& account) { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); accountHistorySubscriptions_.erase(account); } diff --git a/src/libxrpl/server/JSONRPCUtil.cpp b/src/libxrpl/server/JSONRPCUtil.cpp index d32a579d8d..db2ea450a2 100644 --- a/src/libxrpl/server/JSONRPCUtil.cpp +++ b/src/libxrpl/server/JSONRPCUtil.cpp @@ -17,7 +17,7 @@ getHTTPHeaderTimestamp() // sense. There's no point in doing all this work if this function // gets called multiple times a second. char buffer[96]; - time_t now; + time_t now = 0; time(&now); struct tm now_gmt{}; #ifndef _MSC_VER @@ -68,6 +68,7 @@ HTTPReply(int nStatus, std::string const& content, Json::Output const& output, b return; } + // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (nStatus) { case 200: diff --git a/src/libxrpl/server/LoadFeeTrack.cpp b/src/libxrpl/server/LoadFeeTrack.cpp index e3867f36f5..d698e2420e 100644 --- a/src/libxrpl/server/LoadFeeTrack.cpp +++ b/src/libxrpl/server/LoadFeeTrack.cpp @@ -4,6 +4,7 @@ #include #include +#include #include namespace xrpl { @@ -11,7 +12,7 @@ namespace xrpl { bool LoadFeeTrack::raiseLocalFee() { - std::lock_guard sl(lock_); + std::lock_guard const sl(lock_); if (++raiseCount_ < 2) return false; @@ -19,14 +20,12 @@ LoadFeeTrack::raiseLocalFee() std::uint32_t const origFee = localTxnLoadFee_; // make sure this fee takes effect - if (localTxnLoadFee_ < remoteTxnLoadFee_) - localTxnLoadFee_ = remoteTxnLoadFee_; + localTxnLoadFee_ = std::max(localTxnLoadFee_, remoteTxnLoadFee_); // Increase slowly localTxnLoadFee_ += (localTxnLoadFee_ / lftFeeIncFraction); - if (localTxnLoadFee_ > lftFeeMax) - localTxnLoadFee_ = lftFeeMax; + localTxnLoadFee_ = std::min(localTxnLoadFee_, lftFeeMax); if (origFee == localTxnLoadFee_) return false; @@ -38,15 +37,14 @@ LoadFeeTrack::raiseLocalFee() bool LoadFeeTrack::lowerLocalFee() { - std::lock_guard sl(lock_); + std::lock_guard const sl(lock_); std::uint32_t const origFee = localTxnLoadFee_; raiseCount_ = 0; // Reduce slowly localTxnLoadFee_ -= (localTxnLoadFee_ / lftFeeDecFraction); - if (localTxnLoadFee_ < lftNormalFee) - localTxnLoadFee_ = lftNormalFee; + localTxnLoadFee_ = std::max(localTxnLoadFee_, lftNormalFee); if (origFee == localTxnLoadFee_) return false; diff --git a/src/xrpld/app/misc/detail/Manifest.cpp b/src/libxrpl/server/Manifest.cpp similarity index 97% rename from src/xrpld/app/misc/detail/Manifest.cpp rename to src/libxrpl/server/Manifest.cpp index 4526bf6c16..f4634982d0 100644 --- a/src/xrpld/app/misc/detail/Manifest.cpp +++ b/src/libxrpl/server/Manifest.cpp @@ -279,7 +279,7 @@ loadValidatorToken(std::vector const& blob, beast::Journal journal) std::optional ManifestCache::getSigningKey(PublicKey const& pk) const { - std::shared_lock lock{mutex_}; + std::shared_lock const lock{mutex_}; auto const iter = map_.find(pk); if (iter != map_.end() && !iter->second.revoked()) @@ -291,7 +291,7 @@ ManifestCache::getSigningKey(PublicKey const& pk) const PublicKey ManifestCache::getMasterKey(PublicKey const& pk) const { - std::shared_lock lock{mutex_}; + std::shared_lock const lock{mutex_}; if (auto const iter = signingToMasterKeys_.find(pk); iter != signingToMasterKeys_.end()) return iter->second; @@ -302,7 +302,7 @@ ManifestCache::getMasterKey(PublicKey const& pk) const std::optional ManifestCache::getSequence(PublicKey const& pk) const { - std::shared_lock lock{mutex_}; + std::shared_lock const lock{mutex_}; auto const iter = map_.find(pk); if (iter != map_.end() && !iter->second.revoked()) @@ -314,7 +314,7 @@ ManifestCache::getSequence(PublicKey const& pk) const std::optional ManifestCache::getDomain(PublicKey const& pk) const { - std::shared_lock lock{mutex_}; + std::shared_lock const lock{mutex_}; auto const iter = map_.find(pk); if (iter != map_.end() && !iter->second.revoked()) @@ -326,7 +326,7 @@ ManifestCache::getDomain(PublicKey const& pk) const std::optional ManifestCache::getManifest(PublicKey const& pk) const { - std::shared_lock lock{mutex_}; + std::shared_lock const lock{mutex_}; auto const iter = map_.find(pk); if (iter != map_.end() && !iter->second.revoked()) @@ -338,7 +338,7 @@ ManifestCache::getManifest(PublicKey const& pk) const bool ManifestCache::revoked(PublicKey const& pk) const { - std::shared_lock lock{mutex_}; + std::shared_lock const lock{mutex_}; auto const iter = map_.find(pk); if (iter != map_.end()) @@ -437,12 +437,12 @@ ManifestCache::applyManifest(Manifest m) }; { - std::shared_lock sl{mutex_}; + std::shared_lock const sl{mutex_}; if (auto d = prewriteCheck(map_.find(m.masterKey), /*checkSig*/ true, sl)) return *d; } - std::unique_lock sl{mutex_}; + std::unique_lock const sl{mutex_}; auto const iter = map_.find(m.masterKey); // Since we released the previously held read lock, it's possible that the // collections have been written to. This means we need to run @@ -562,7 +562,7 @@ ManifestCache::save( std::string const& dbTable, std::function const& isTrusted) { - std::shared_lock lock{mutex_}; + std::shared_lock const lock{mutex_}; auto db = dbCon.checkoutDb(); saveManifests(*db, dbTable, isTrusted, map_, j_); diff --git a/src/libxrpl/server/Port.cpp b/src/libxrpl/server/Port.cpp index 9e73d9f007..2d0b4ec257 100644 --- a/src/libxrpl/server/Port.cpp +++ b/src/libxrpl/server/Port.cpp @@ -44,7 +44,7 @@ operator<<(std::ostream& os, Port const& p) { os << "'" << p.name << "' (ip=" << p.ip << ":" << p.port << ", "; - if (p.admin_nets_v4.size() || p.admin_nets_v6.size()) + if (!p.admin_nets_v4.empty() || !p.admin_nets_v6.empty()) { os << "admin nets:"; for (auto const& net : p.admin_nets_v4) @@ -59,7 +59,7 @@ operator<<(std::ostream& os, Port const& p) } } - if (p.secure_gateway_nets_v4.size() || p.secure_gateway_nets_v6.size()) + if (!p.secure_gateway_nets_v4.empty() || !p.secure_gateway_nets_v6.empty()) { os << "secure_gateway nets:"; for (auto const& net : p.secure_gateway_nets_v4) @@ -98,7 +98,7 @@ populate( while (std::getline(ss, ip, ',')) { boost::algorithm::trim(ip); - bool v4; + bool v4 = false; boost::asio::ip::network_v4 v4Net; boost::asio::ip::network_v6 v6Net; diff --git a/src/libxrpl/server/State.cpp b/src/libxrpl/server/State.cpp index f41a0c61b3..a7c0c0fca2 100644 --- a/src/libxrpl/server/State.cpp +++ b/src/libxrpl/server/State.cpp @@ -31,7 +31,7 @@ initStateDB(soci::session& session, BasicConfig const& config, std::string const count = *countO; } - if (!count) + if (count == 0) { session << "INSERT INTO DbState VALUES (1, '', '', 0);"; } @@ -45,7 +45,7 @@ initStateDB(soci::session& session, BasicConfig const& config, std::string const count = *countO; } - if (!count) + if (count == 0) { session << "INSERT INTO CanDelete VALUES (1, 0);"; } @@ -54,7 +54,7 @@ initStateDB(soci::session& session, BasicConfig const& config, std::string const LedgerIndex getCanDelete(soci::session& session) { - LedgerIndex seq; + LedgerIndex seq = 0; session << "SELECT CanDeleteSeq FROM CanDelete WHERE Key = 1;", soci::into(seq); ; return seq; diff --git a/src/libxrpl/server/Vacuum.cpp b/src/libxrpl/server/Vacuum.cpp index cb31c6fa7a..80de45cf66 100644 --- a/src/libxrpl/server/Vacuum.cpp +++ b/src/libxrpl/server/Vacuum.cpp @@ -9,10 +9,10 @@ namespace xrpl { bool doVacuumDB(DatabaseCon::Setup const& setup, beast::Journal j) { - boost::filesystem::path dbPath = setup.dataDir / TxDBName; + boost::filesystem::path const dbPath = setup.dataDir / TxDBName; uintmax_t const dbSize = file_size(dbPath); - XRPL_ASSERT(dbSize != static_cast(-1), "ripple:doVacuumDB : file_size succeeded"); + XRPL_ASSERT(dbSize != static_cast(-1), "xrpl::doVacuumDB : file_size succeeded"); if (auto available = space(dbPath.parent_path()).available; available < dbSize) { @@ -25,7 +25,7 @@ doVacuumDB(DatabaseCon::Setup const& setup, beast::Journal j) auto txnDB = std::make_unique(setup, TxDBName, setup.txPragma, TxDBInit, j); auto& session = txnDB->getSession(); - std::uint32_t pageSize; + std::uint32_t pageSize = 0; // Only the most trivial databases will fit in memory on typical // (recommended) hardware. Force temp files to be written to disk @@ -36,7 +36,7 @@ doVacuumDB(DatabaseCon::Setup const& setup, beast::Journal j) std::cout << "VACUUM beginning. page_size: " << pageSize << std::endl; session << "VACUUM;"; - XRPL_ASSERT(setup.globalPragma, "ripple:doVacuumDB : non-null global pragma"); + XRPL_ASSERT(setup.globalPragma, "xrpl::doVacuumDB : non-null global pragma"); for (auto const& p : *setup.globalPragma) session << p; session << "PRAGMA page_size;", soci::into(pageSize); diff --git a/src/libxrpl/server/Wallet.cpp b/src/libxrpl/server/Wallet.cpp index 95c2e89a20..8da934a921 100644 --- a/src/libxrpl/server/Wallet.cpp +++ b/src/libxrpl/server/Wallet.cpp @@ -199,13 +199,13 @@ bool createFeatureVotes(soci::session& session) { soci::transaction tr(session); - std::string sql = + std::string const sql = "SELECT count(*) FROM sqlite_master " "WHERE type='table' AND name='FeatureVotes'"; // SOCI requires boost::optional (not std::optional) as the parameter. boost::optional featureVotesCount; session << sql, soci::into(featureVotesCount); - bool exists = static_cast(*featureVotesCount); + bool const exists = static_cast(*featureVotesCount); // Create FeatureVotes table in WalletDB if it doesn't exist if (!exists) @@ -232,8 +232,8 @@ readAmendments( return safe_cast(dbVote.value_or(1)); }; - soci::transaction tr(session); - std::string sql = + soci::transaction const tr(session); + std::string const sql = "SELECT AmendmentHash, AmendmentName, Veto FROM " "( SELECT AmendmentHash, AmendmentName, Veto, RANK() OVER " "( PARTITION BY AmendmentHash ORDER BY ROWID DESC ) " diff --git a/src/libxrpl/shamap/SHAMap.cpp b/src/libxrpl/shamap/SHAMap.cpp index 15457b2d2e..505d20651e 100644 --- a/src/libxrpl/shamap/SHAMap.cpp +++ b/src/libxrpl/shamap/SHAMap.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -84,11 +85,11 @@ SHAMap::dirtyUp( while (!stack.empty()) { auto node = intr_ptr::dynamic_pointer_cast(stack.top().first); - SHAMapNodeID nodeID = stack.top().second; + SHAMapNodeID const nodeID = stack.top().second; stack.pop(); XRPL_ASSERT(node, "xrpl::SHAMap::dirtyUp : non-null node"); - int branch = selectBranch(nodeID, target); + int const branch = selectBranch(nodeID, target); XRPL_ASSERT(branch >= 0, "xrpl::SHAMap::dirtyUp : valid branch"); node = unshareNode(std::move(node), nodeID); @@ -122,14 +123,14 @@ SHAMap::walkTowardsKey(uint256 const& id, SharedPtrNodeStack* stack) const if (stack != nullptr) stack->push({inNode, nodeID}); - return static_cast(inNode.get()); + return safe_downcast(inNode.get()); } SHAMapLeafNode* SHAMap::findKey(uint256 const& id) const { - SHAMapLeafNode* leaf = walkTowardsKey(id); - if (leaf && leaf->peekItem()->key() != id) + SHAMapLeafNode* leaf = walkTowardsKey(id); // NOLINT(misc-const-correctness) + if ((leaf != nullptr) && leaf->peekItem()->key() != id) leaf = nullptr; return leaf; } @@ -220,7 +221,7 @@ SHAMap::fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter* filter) const } } - if (filter) + if (filter != nullptr) node = checkFilter(hash, filter); return node; @@ -252,9 +253,9 @@ SHAMap::fetchNode(SHAMapHash const& hash) const SHAMapTreeNode* SHAMap::descendThrow(SHAMapInnerNode* parent, int branch) const { - SHAMapTreeNode* ret = descend(parent, branch); + SHAMapTreeNode* ret = descend(parent, branch); // NOLINT(misc-const-correctness) - if (!ret && !parent->isEmptyBranch(branch)) + if ((ret == nullptr) && !parent->isEmptyBranch(branch)) Throw(type_, parent->getChildHash(branch)); return ret; @@ -274,8 +275,8 @@ SHAMap::descendThrow(SHAMapInnerNode& parent, int branch) const SHAMapTreeNode* SHAMap::descend(SHAMapInnerNode* parent, int branch) const { - SHAMapTreeNode* ret = parent->getChildPointer(branch); - if (ret || !backed_) + SHAMapTreeNode* ret = parent->getChildPointer(branch); // NOLINT(misc-const-correctness) + if ((ret != nullptr) || !backed_) return ret; intr_ptr::SharedPtr node = fetchNodeNT(parent->getChildHash(branch)); @@ -325,9 +326,9 @@ SHAMap::descend( XRPL_ASSERT( !parent->isEmptyBranch(branch), "xrpl::SHAMap::descend : parent branch is non-empty"); - SHAMapTreeNode* child = parent->getChildPointer(branch); + SHAMapTreeNode* child = parent->getChildPointer(branch); // NOLINT(misc-const-correctness) - if (!child) + if (child == nullptr) { auto const& childHash = parent->getChildHash(branch); intr_ptr::SharedPtr childNode = fetchNodeNT(childHash, filter); @@ -352,8 +353,8 @@ SHAMap::descendAsync( { pending = false; - SHAMapTreeNode* ret = parent->getChildPointer(branch); - if (ret) + SHAMapTreeNode* ret = parent->getChildPointer(branch); // NOLINT(misc-const-correctness) + if (ret != nullptr) return ret; auto const& hash = parent->getChildHash(branch); @@ -361,7 +362,7 @@ SHAMap::descendAsync( auto ptr = cacheLookup(hash); if (!ptr) { - if (filter) + if (filter != nullptr) ptr = checkFilter(hash, filter); if (!ptr && backed_) @@ -417,9 +418,13 @@ SHAMap::belowHelper( } auto inner = intr_ptr::static_pointer_cast(node); if (stack.empty()) + { stack.push({inner, SHAMapNodeID{}}); + } else + { stack.push({inner, stack.top().second.getChildNodeID(branch)}); + } for (int i = init; cmp(i);) { if (!inner->isEmptyBranch(i)) @@ -437,7 +442,9 @@ SHAMap::belowHelper( i = init; // descend and reset loop } else + { incr(i); // scan next branch + } } return nullptr; } @@ -471,19 +478,19 @@ SHAMap::onlyBelow(SHAMapTreeNode* node) const while (!node->isLeaf()) { SHAMapTreeNode* nextNode = nullptr; - auto inner = static_cast(node); + auto inner = safe_downcast(node); for (int i = 0; i < branchFactor; ++i) { if (!inner->isEmptyBranch(i)) { - if (nextNode) + if (nextNode != nullptr) return no_item; nextNode = descendThrow(inner, i); } } - if (!nextNode) + if (nextNode == nullptr) { // LCOV_EXCL_START UNREACHABLE("xrpl::SHAMap::onlyBelow : no next node"); @@ -496,7 +503,7 @@ SHAMap::onlyBelow(SHAMapTreeNode* node) const // An inner node must have at least one leaf // below it, unless it's the root_ - auto const leaf = static_cast(node); + auto const leaf = safe_downcast(node); XRPL_ASSERT( leaf->peekItem() || (leaf == root_.get()), "xrpl::SHAMap::onlyBelow : valid inner node"); return leaf->peekItem(); @@ -506,8 +513,8 @@ SHAMapLeafNode const* SHAMap::peekFirstItem(SharedPtrNodeStack& stack) const { XRPL_ASSERT(stack.empty(), "xrpl::SHAMap::peekFirstItem : empty stack input"); - SHAMapLeafNode* node = firstBelow(root_, stack); - if (!node) + SHAMapLeafNode const* node = firstBelow(root_, stack); + if (node == nullptr) { while (!stack.empty()) stack.pop(); @@ -533,7 +540,7 @@ SHAMap::peekNextItem(uint256 const& id, SharedPtrNodeStack& stack) const { node = descendThrow(*inner, i); auto leaf = firstBelow(node, stack, i); - if (!leaf) + if (leaf == nullptr) Throw(type_, id); XRPL_ASSERT(leaf->isLeaf(), "xrpl::SHAMap::peekNextItem : leaf is valid"); return leaf; @@ -548,9 +555,9 @@ SHAMap::peekNextItem(uint256 const& id, SharedPtrNodeStack& stack) const boost::intrusive_ptr const& SHAMap::peekItem(uint256 const& id) const { - SHAMapLeafNode* leaf = findKey(id); + SHAMapLeafNode const* leaf = findKey(id); - if (!leaf) + if (leaf == nullptr) return no_item; return leaf->peekItem(); @@ -559,9 +566,9 @@ SHAMap::peekItem(uint256 const& id) const boost::intrusive_ptr const& SHAMap::peekItem(uint256 const& id, SHAMapHash& hash) const { - SHAMapLeafNode* leaf = findKey(id); + SHAMapLeafNode const* leaf = findKey(id); - if (!leaf) + if (leaf == nullptr) return no_item; hash = leaf->getHash(); @@ -578,7 +585,7 @@ SHAMap::upper_bound(uint256 const& id) const auto [node, nodeID] = stack.top(); if (node->isLeaf()) { - auto leaf = static_cast(node.get()); + auto leaf = safe_downcast(node.get()); if (leaf->peekItem()->key() > id) return const_iterator(this, leaf->peekItem().get(), std::move(stack)); } @@ -591,7 +598,7 @@ SHAMap::upper_bound(uint256 const& id) const { node = descendThrow(*inner, branch); auto leaf = firstBelow(node, stack, branch); - if (!leaf) + if (leaf == nullptr) Throw(type_, id); return const_iterator(this, leaf->peekItem().get(), std::move(stack)); } @@ -611,7 +618,7 @@ SHAMap::lower_bound(uint256 const& id) const auto [node, nodeID] = stack.top(); if (node->isLeaf()) { - auto leaf = static_cast(node.get()); + auto leaf = safe_downcast(node.get()); if (leaf->peekItem()->key() < id) return const_iterator(this, leaf->peekItem().get(), std::move(stack)); } @@ -624,7 +631,7 @@ SHAMap::lower_bound(uint256 const& id) const { node = descendThrow(*inner, branch); auto leaf = lastBelow(node, stack, branch); - if (!leaf) + if (leaf == nullptr) Throw(type_, id); return const_iterator(this, leaf->peekItem().get(), std::move(stack)); } @@ -660,20 +667,26 @@ SHAMap::delItem(uint256 const& id) if (!leaf || (leaf->peekItem()->key() != id)) return false; - SHAMapNodeType type = leaf->getType(); + SHAMapNodeType const type = leaf->getType(); - // What gets attached to the end of the chain - // (For now, nothing, since we deleted the leaf) - intr_ptr::SharedPtr prevNode; + using TreeNodeType = intr_ptr::SharedPtr; + + // What gets attached to the end of the chain (For now, nothing, since we deleted the leaf) + TreeNodeType prevNode; while (!stack.empty()) { auto node = intr_ptr::static_pointer_cast(stack.top().first); - SHAMapNodeID nodeID = stack.top().second; + SHAMapNodeID const nodeID = stack.top().second; stack.pop(); node = unshareNode(std::move(node), nodeID); - node->setChild(selectBranch(nodeID, id), std::move(prevNode)); + node->setChild( + selectBranch(nodeID, id), std::move(prevNode)); // NOLINT(bugprone-use-after-move) + + XRPL_ASSERT( + not prevNode, // NOLINT(bugprone-use-after-move) + "xrpl::SHAMap::delItem : prevNode should be nullptr after std::move"); if (!nodeID.isRoot()) { @@ -683,7 +696,9 @@ SHAMap::delItem(uint256 const& id) if (bc == 0) { // no children below this branch - prevNode.reset(); + // + // Note: This is unnecessary due to the std::move above but left here for safety + prevNode = TreeNodeType{}; } else if (bc == 1) { @@ -696,7 +711,7 @@ SHAMap::delItem(uint256 const& id) { if (!node->isEmptyBranch(i)) { - node->setChild(i, intr_ptr::SharedPtr{}); + node->setChild(i, TreeNodeType{}); break; } } @@ -726,7 +741,7 @@ SHAMap::addGiveItem(SHAMapNodeType type, boost::intrusive_ptr XRPL_ASSERT(type != SHAMapNodeType::tnINNER, "xrpl::SHAMap::addGiveItem : valid type input"); // add the specified item, does not update - uint256 tag = item->key(); + uint256 const tag = item->key(); SharedPtrNodeStack stack; walkTowardsKey(tag, &stack); @@ -748,7 +763,7 @@ SHAMap::addGiveItem(SHAMapNodeType type, boost::intrusive_ptr { // easy case, we end on an inner node auto inner = intr_ptr::static_pointer_cast(node); - int branch = selectBranch(nodeID, tag); + int const branch = selectBranch(nodeID, tag); XRPL_ASSERT( inner->isEmptyBranch(branch), "xrpl::SHAMap::addGiveItem : inner branch is empty"); inner->setChild(branch, makeTypedLeaf(type, std::move(item), cowid_)); @@ -764,7 +779,7 @@ SHAMap::addGiveItem(SHAMapNodeType type, boost::intrusive_ptr node = intr_ptr::make_shared(node->cowid()); - unsigned int b1, b2; + unsigned int b1 = 0, b2 = 0; while ((b1 = selectBranch(nodeID, tag)) == (b2 = selectBranch(nodeID, otherItem->key()))) { @@ -779,7 +794,7 @@ SHAMap::addGiveItem(SHAMapNodeType type, boost::intrusive_ptr // we can add the two leaf nodes here XRPL_ASSERT(node->isInner(), "xrpl::SHAMap::addGiveItem : node is inner"); - auto inner = static_cast(node.get()); + auto inner = safe_downcast(node.get()); inner->setChild(b1, makeTypedLeaf(type, std::move(item), cowid_)); inner->setChild(b2, makeTypedLeaf(type, std::move(otherItem), cowid_)); } @@ -810,7 +825,7 @@ bool SHAMap::updateGiveItem(SHAMapNodeType type, boost::intrusive_ptr item) { // can't change the tag but can change the hash - uint256 tag = item->key(); + uint256 const tag = item->key(); XRPL_ASSERT(state_ != SHAMapState::Immutable, "xrpl::SHAMap::updateGiveItem : not immutable"); @@ -980,7 +995,7 @@ SHAMap::walkSubTree(bool doWrite, NodeObjectType t) int pos = 0; // We can't flush an inner node until we flush its children - while (1) + while (true) { while (pos < branchFactor) { @@ -992,7 +1007,7 @@ SHAMap::walkSubTree(bool doWrite, NodeObjectType t) { // No need to do I/O. If the node isn't linked, // it can't need to be flushed - int branch = pos; + int const branch = pos; auto child = node->getChild(pos++); if (child && (child->cowid() != 0)) @@ -1006,10 +1021,7 @@ SHAMap::walkSubTree(bool doWrite, NodeObjectType t) // save our place and work on this node stack.emplace(std::move(node), branch); - // The semantics of this changes when we move to c++-20 - // Right now no move will occur; With c++-20 child will - // be moved from. - node = intr_ptr::static_pointer_cast(std::move(child)); + node = intr_ptr::static_pointer_cast(child); pos = 0; } else @@ -1088,13 +1100,13 @@ SHAMap::dump(bool hash) const if (node->isInner()) { - auto inner = static_cast(node); + auto inner = safe_downcast(node); for (int i = 0; i < branchFactor; ++i) { if (!inner->isEmptyBranch(i)) { auto child = inner->getChildPointer(i); - if (child) + if (child != nullptr) { XRPL_ASSERT( child->getHash() == inner->getChildHash(i), @@ -1105,7 +1117,9 @@ SHAMap::dump(bool hash) const } } else + { ++leafCount; + } } while (!stack.empty()); JLOG(journal_.info()) << leafCount << " resident leaves"; diff --git a/src/libxrpl/shamap/SHAMapDelta.cpp b/src/libxrpl/shamap/SHAMapDelta.cpp index 9a6342343f..ded57760f1 100644 --- a/src/libxrpl/shamap/SHAMapDelta.cpp +++ b/src/libxrpl/shamap/SHAMapDelta.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -39,23 +40,29 @@ SHAMap::walkBranch( if (node->isInner()) { // This is an inner node, add all non-empty branches - auto inner = static_cast(node); + auto inner = safe_downcast(node); for (int i = 0; i < 16; ++i) + { if (!inner->isEmptyBranch(i)) nodeStack.push({descendThrow(inner, i)}); + } } else { // This is a leaf node, process its item - auto item = static_cast(node)->peekItem(); + auto item = safe_downcast(node)->peekItem(); if (emptyBranch || (item->key() != otherMapItem->key())) { // unmatched if (isFirstMap) + { differences.insert(std::make_pair(item->key(), DeltaRef(item, nullptr))); + } else + { differences.insert(std::make_pair(item->key(), DeltaRef(nullptr, item))); + } if (--maxCount <= 0) return false; @@ -64,9 +71,13 @@ SHAMap::walkBranch( { // non-matching items with same tag if (isFirstMap) + { differences.insert(std::make_pair(item->key(), DeltaRef(item, otherMapItem))); + } else + { differences.insert(std::make_pair(item->key(), DeltaRef(otherMapItem, item))); + } if (--maxCount <= 0) return false; @@ -84,12 +95,16 @@ SHAMap::walkBranch( if (!emptyBranch) { // otherMapItem was unmatched, must add - if (isFirstMap) // this is first map, so other item is from second + if (isFirstMap) + { // this is first map, so other item is from second differences.insert( std::make_pair(otherMapItem->key(), DeltaRef(nullptr, otherMapItem))); + } else + { differences.insert( std::make_pair(otherMapItem->key(), DeltaRef(otherMapItem, nullptr))); + } if (--maxCount <= 0) return false; @@ -121,7 +136,7 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const auto [ourNode, otherNode] = nodeStack.top(); nodeStack.pop(); - if (!ourNode || !otherNode) + if ((ourNode == nullptr) || (otherNode == nullptr)) { // LCOV_EXCL_START UNREACHABLE("xrpl::SHAMap::compare : missing a node"); @@ -132,8 +147,8 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const if (ourNode->isLeaf() && otherNode->isLeaf()) { // two leaves - auto ours = static_cast(ourNode); - auto other = static_cast(otherNode); + auto ours = safe_downcast(ourNode); + auto other = safe_downcast(otherNode); if (ours->peekItem()->key() == other->peekItem()->key()) { if (ours->peekItem()->slice() != other->peekItem()->slice()) @@ -161,23 +176,24 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const } else if (ourNode->isInner() && otherNode->isLeaf()) { - auto ours = static_cast(ourNode); - auto other = static_cast(otherNode); + auto ours = safe_downcast(ourNode); + auto other = safe_downcast(otherNode); if (!walkBranch(ours, other->peekItem(), true, differences, maxCount)) return false; } else if (ourNode->isLeaf() && otherNode->isInner()) { - auto ours = static_cast(ourNode); - auto other = static_cast(otherNode); + auto ours = safe_downcast(ourNode); + auto other = safe_downcast(otherNode); if (!otherMap.walkBranch(other, ours->peekItem(), false, differences, maxCount)) return false; } else if (ourNode->isInner() && otherNode->isInner()) { - auto ours = static_cast(ourNode); - auto other = static_cast(otherNode); + auto ours = safe_downcast(ourNode); + auto other = safe_downcast(otherNode); for (int i = 0; i < 16; ++i) + { if (ours->getChildHash(i) != other->getChildHash(i)) { if (other->isEmptyBranch(i)) @@ -194,9 +210,12 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const if (!otherMap.walkBranch(iNode, nullptr, false, differences, maxCount)) return false; } - else // The two trees have different non-empty branches + else + { // The two trees have different non-empty branches nodeStack.push({descendThrow(ours, i), otherMap.descendThrow(other, i)}); + } } + } } else { @@ -222,14 +241,14 @@ SHAMap::walkMap(std::vector& missingNodes, int maxMissing) co while (!nodeStack.empty()) { - intr_ptr::SharedPtr node = std::move(nodeStack.top()); + intr_ptr::SharedPtr const node = std::move(nodeStack.top()); nodeStack.pop(); for (int i = 0; i < 16; ++i) { if (!node->isEmptyBranch(i)) { - intr_ptr::SharedPtr nextNode = descendNoStore(*node, i); + intr_ptr::SharedPtr const nextNode = descendNoStore(*node, i); if (nextNode) { @@ -291,7 +310,8 @@ SHAMap::walkMapParallel(std::vector& missingNodes, int maxMis { while (!nodeStack.empty()) { - intr_ptr::SharedPtr node = std::move(nodeStack.top()); + intr_ptr::SharedPtr const node = + std::move(nodeStack.top()); XRPL_ASSERT(node, "xrpl::SHAMap::walkMapParallel : non-null node"); nodeStack.pop(); @@ -299,19 +319,21 @@ SHAMap::walkMapParallel(std::vector& missingNodes, int maxMis { if (node->isEmptyBranch(i)) continue; - intr_ptr::SharedPtr nextNode = + intr_ptr::SharedPtr const nextNode = descendNoStore(*node, i); if (nextNode) { if (nextNode->isInner()) + { nodeStack.push( intr_ptr::static_pointer_cast( nextNode)); + } } else { - std::lock_guard l{m}; + std::lock_guard const l{m}; missingNodes.emplace_back(type_, node->getChildHash(i)); if (--maxMissing <= 0) return; @@ -321,7 +343,7 @@ SHAMap::walkMapParallel(std::vector& missingNodes, int maxMis } catch (SHAMapMissingNode const& e) { - std::lock_guard l(m); + std::lock_guard const l(m); exceptions.push_back(e); } }, @@ -331,7 +353,7 @@ SHAMap::walkMapParallel(std::vector& missingNodes, int maxMis for (std::thread& worker : workers) worker.join(); - std::lock_guard l(m); + std::lock_guard const l(m); if (exceptions.empty()) return true; std::stringstream ss; diff --git a/src/libxrpl/shamap/SHAMapInnerNode.cpp b/src/libxrpl/shamap/SHAMapInnerNode.cpp index 7c344d9264..91249d139e 100644 --- a/src/libxrpl/shamap/SHAMapInnerNode.cpp +++ b/src/libxrpl/shamap/SHAMapInnerNode.cpp @@ -20,7 +20,7 @@ SHAMapInnerNode::~SHAMapInnerNode() = default; void SHAMapInnerNode::partialDestructor() { - intr_ptr::SharedPtr* children; + intr_ptr::SharedPtr* 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(); }); @@ -61,8 +61,8 @@ SHAMapInnerNode::clone(std::uint32_t cowid) const p->hash_ = hash_; p->isBranch_ = isBranch_; p->fullBelowGen_ = fullBelowGen_; - SHAMapHash *cloneHashes, *thisHashes; - intr_ptr::SharedPtr*cloneChildren, *thisChildren; + SHAMapHash *cloneHashes = nullptr, *thisHashes = nullptr; + intr_ptr::SharedPtr*cloneChildren = nullptr, *thisChildren = nullptr; // structured bindings can't be captured in c++ 17; use tie instead std::tie(std::ignore, cloneHashes, cloneChildren) = p->hashesAndChildren_.getHashesAndChildren(); @@ -82,7 +82,7 @@ SHAMapInnerNode::clone(std::uint32_t cowid) const } spinlock sl(lock_); - std::lock_guard lock(sl); + std::lock_guard const lock(sl); if (thisIsSparse) { @@ -125,9 +125,13 @@ SHAMapInnerNode::makeFullInner(Slice data, SHAMapHash const& hash, bool hashVali ret->resizeChildArrays(ret->getBranchCount()); if (hashValid) + { ret->hash_ = hash; + } else + { ret->updateHash(); + } return ret; } @@ -185,8 +189,8 @@ SHAMapInnerNode::updateHash() void SHAMapInnerNode::updateHashDeep() { - SHAMapHash* hashes; - intr_ptr::SharedPtr* children; + SHAMapHash* hashes = nullptr; + intr_ptr::SharedPtr* 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) { @@ -253,9 +257,11 @@ SHAMapInnerNode::setChild(int m, intr_ptr::SharedPtr child) auto const dstIsBranch = [&] { if (child) + { return isBranch_ | (1u << m); - else - return isBranch_ & ~(1u << m); + } + + return isBranch_ & ~(1u << m); }(); auto const dstToAllocate = popcnt16(dstIsBranch); @@ -307,7 +313,7 @@ SHAMapInnerNode::getChildPointer(int branch) auto const index = *getChildIndex(branch); packed_spinlock sl(lock_, index); - std::lock_guard lock(sl); + std::lock_guard const lock(sl); return hashesAndChildren_.getChildren()[index].get(); } @@ -322,7 +328,7 @@ SHAMapInnerNode::getChild(int branch) auto const index = *getChildIndex(branch); packed_spinlock sl(lock_, index); - std::lock_guard lock(sl); + std::lock_guard const lock(sl); return hashesAndChildren_.getChildren()[index]; } @@ -355,7 +361,7 @@ SHAMapInnerNode::canonicalizeChild(int branch, intr_ptr::SharedPtrkey()); diff --git a/src/libxrpl/shamap/SHAMapNodeID.cpp b/src/libxrpl/shamap/SHAMapNodeID.cpp index f7cc80e250..e5dbebc871 100644 --- a/src/libxrpl/shamap/SHAMapNodeID.cpp +++ b/src/libxrpl/shamap/SHAMapNodeID.cpp @@ -76,7 +76,7 @@ SHAMapNodeID::getChildNodeID(unsigned int m) const Throw("Incorrect mask for " + to_string(*this)); SHAMapNodeID node{depth_ + 1, id_}; - node.id_.begin()[depth_ / 2] |= (depth_ & 1) ? m : (m << 4); + node.id_.begin()[depth_ / 2] |= ((depth_ & 1) != 0u) ? m : (m << 4); return node; } @@ -87,7 +87,7 @@ deserializeSHAMapNodeID(void const* data, std::size_t size) if (size == 33) { - unsigned int depth = *(static_cast(data) + 32); + unsigned int const depth = *(static_cast(data) + 32); if (depth <= SHAMap::leafDepth) { auto const id = uint256::fromVoid(data); @@ -106,10 +106,14 @@ selectBranch(SHAMapNodeID const& id, uint256 const& hash) auto const depth = id.getDepth(); auto branch = static_cast(*(hash.begin() + (depth / 2))); - if (depth & 1) + if ((depth & 1) != 0u) + { branch &= 0xf; + } else + { branch >>= 4; + } XRPL_ASSERT(branch < SHAMap::branchFactor, "xrpl::selectBranch : maximum result"); return branch; diff --git a/src/libxrpl/shamap/SHAMapSync.cpp b/src/libxrpl/shamap/SHAMapSync.cpp index d80ddb1e43..2074b43cb4 100644 --- a/src/libxrpl/shamap/SHAMapSync.cpp +++ b/src/libxrpl/shamap/SHAMapSync.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -12,7 +13,7 @@ SHAMap::visitLeaves( { visitNodes([&leafFunction](SHAMapTreeNode& node) { if (!node.isInner()) - leafFunction(static_cast(node).peekItem()); + leafFunction(safe_downcast(node).peekItem()); return true; }); } @@ -40,12 +41,14 @@ SHAMap::visitNodes(std::function const& function) const { if (!node->isEmptyBranch(pos)) { - intr_ptr::SharedPtr child = descendNoStore(*node, pos); + intr_ptr::SharedPtr const child = descendNoStore(*node, pos); if (!function(*child)) return; if (child->isLeaf()) + { ++pos; + } else { // If there are no more children, don't push this node @@ -90,13 +93,13 @@ SHAMap::visitDifferences( if (root_->getHash().isZero()) return; - if (have && (root_->getHash() == have->root_->getHash())) + if ((have != nullptr) && (root_->getHash() == have->root_->getHash())) return; if (root_->isLeaf()) { auto leaf = intr_ptr::static_pointer_cast(root_); - if (!have || !have->hasLeafNode(leaf->peekItem()->key(), leaf->getHash())) + if ((have == nullptr) || !have->hasLeafNode(leaf->peekItem()->key(), leaf->getHash())) function(*root_); return; } @@ -104,7 +107,7 @@ SHAMap::visitDifferences( using StackEntry = std::pair; std::stack> stack; - stack.push({static_cast(root_.get()), SHAMapNodeID{}}); + stack.push({safe_downcast(root_.get()), SHAMapNodeID{}}); while (!stack.empty()) { @@ -121,18 +124,18 @@ SHAMap::visitDifferences( if (!node->isEmptyBranch(i)) { auto const& childHash = node->getChildHash(i); - SHAMapNodeID childID = nodeID.getChildNodeID(i); + SHAMapNodeID const childID = nodeID.getChildNodeID(i); auto next = descendThrow(node, i); if (next->isInner()) { - if (!have || !have->hasInnerNode(childID, childHash)) - stack.push({static_cast(next), childID}); + if ((have == nullptr) || !have->hasInnerNode(childID, childHash)) + stack.push({safe_downcast(next), childID}); } else if ( - !have || + (have == nullptr) || !have->hasLeafNode( - static_cast(next)->peekItem()->key(), childHash)) + safe_downcast(next)->peekItem()->key(), childHash)) { if (!function(*next)) return; @@ -157,13 +160,13 @@ SHAMap::gmn_ProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se) while (currentChild < 16) { - int branch = (firstChild + currentChild++) % 16; + int const branch = (firstChild + currentChild++) % 16; if (node->isEmptyBranch(branch)) continue; auto const& childHash = node->getChildHash(branch); - if (mn.missingHashes_.count(childHash) != 0) + if (mn.missingHashes_.contains(childHash)) { // we already know this child node is missing fullBelow = false; @@ -179,7 +182,7 @@ SHAMap::gmn_ProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se) [node, nodeID, branch, &mn]( intr_ptr::SharedPtr found, SHAMapHash const&) { // a read completed asynchronously - std::unique_lock lock{mn.deferLock_}; + std::unique_lock const lock{mn.deferLock_}; mn.finishedReads_.emplace_back(node, nodeID, branch, std::move(found)); mn.deferCondVar_.notify_one(); }); @@ -189,7 +192,7 @@ SHAMap::gmn_ProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se) fullBelow = false; ++mn.deferred_; } - else if (!d) + else if (d == nullptr) { // node is not in database @@ -201,12 +204,13 @@ SHAMap::gmn_ProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se) if (--mn.max_ <= 0) return; } - else if (d->isInner() && !static_cast(d)->isFullBelow(mn.generation_)) + else if ( + d->isInner() && !safe_downcast(d)->isFullBelow(mn.generation_)) { mn.stack_.push(se); // Switch to processing the child node - node = static_cast(d); + node = safe_downcast(d); nodeID = nodeID.getChildNodeID(branch); firstChild = rand_int(255); currentChild = 0; @@ -305,7 +309,7 @@ SHAMap::getMissingNodes(int max, SHAMapSyncFilter* filter) // that the two threads will produce different request sets (which is // more efficient than sending identical requests). MissingNodes::StackEntry pos{ - static_cast(root_.get()), SHAMapNodeID(), rand_int(255), 0, true}; + safe_downcast(root_.get()), SHAMapNodeID(), rand_int(255), 0, true}; auto& node = std::get<0>(pos); auto& nextChild = std::get<3>(pos); auto& fullBelow = std::get<4>(pos); @@ -323,7 +327,7 @@ SHAMap::getMissingNodes(int max, SHAMapSyncFilter* filter) if ((node == nullptr) && !mn.stack_.empty()) { // Pick up where we left off with this node's parent - bool was = fullBelow; // was full below + bool const was = fullBelow; // was full below pos = mn.stack_.top(); mn.stack_.pop(); @@ -343,7 +347,7 @@ SHAMap::getMissingNodes(int max, SHAMapSyncFilter* filter) // We have either emptied the stack or // posted as many deferred reads as we can - if (mn.deferred_) + if (mn.deferred_ != 0) gmn_ProcessDeferredReads(mn); if (mn.max_ <= 0) @@ -356,8 +360,10 @@ SHAMap::getMissingNodes(int max, SHAMapSyncFilter* filter) { // Recheck nodes we could not finish before for (auto const& [innerNode, nodeId] : mn.resumes_) + { if (!innerNode->isFullBelow(mn.generation_)) mn.stack_.push(std::make_tuple(innerNode, nodeId, rand_int(255), 0, true)); + } mn.resumes_.clear(); } @@ -396,10 +402,10 @@ SHAMap::getNodeFat( auto node = root_.get(); SHAMapNodeID nodeID; - while (node && node->isInner() && (nodeID.getDepth() < wanted.getDepth())) + while ((node != nullptr) && node->isInner() && (nodeID.getDepth() < wanted.getDepth())) { - int branch = selectBranch(nodeID, wanted.getNodeID()); - auto inner = static_cast(node); + int const branch = selectBranch(nodeID, wanted.getNodeID()); + auto inner = safe_downcast(node); if (inner->isEmptyBranch(branch)) return false; node = descendThrow(inner, branch); @@ -413,7 +419,7 @@ SHAMap::getNodeFat( return false; } - if (node->isInner() && static_cast(node)->isEmpty()) + if (node->isInner() && safe_downcast(node)->isEmpty()) { JLOG(journal_.warn()) << "peer requests empty node"; return false; @@ -438,8 +444,8 @@ SHAMap::getNodeFat( { // We descend inner nodes with only a single child // without decrementing the depth - auto inner = static_cast(node); - int bc = inner->getBranchCount(); + auto inner = safe_downcast(node); + int const bc = inner->getBranchCount(); if ((depth > 0) || (bc == 1)) { @@ -503,7 +509,7 @@ SHAMap::addRootNode(SHAMapHash const& hash, Slice const& rootNode, SHAMapSyncFil if (root_->isLeaf()) clearSynching(); - if (filter) + if (filter != nullptr) { Serializer s; root_->serializeWithPrefix(s); @@ -530,12 +536,12 @@ SHAMap::addKnownNode(SHAMapNodeID const& node, Slice const& rawNode, SHAMapSyncF auto currNode = root_.get(); while (currNode->isInner() && - !static_cast(currNode)->isFullBelow(generation) && + !safe_downcast(currNode)->isFullBelow(generation) && (currNodeID.getDepth() < node.getDepth())) { int const branch = selectBranch(currNodeID, node.getNodeID()); XRPL_ASSERT(branch >= 0, "xrpl::SHAMap::addKnownNode : valid branch"); - auto inner = static_cast(currNode); + auto inner = safe_downcast(currNode); if (inner->isEmptyBranch(branch)) { JLOG(journal_.warn()) << "Add known node for empty branch" << node; @@ -570,7 +576,7 @@ SHAMap::addKnownNode(SHAMapNodeID const& node, Slice const& rawNode, SHAMapSyncF if (newNode->isLeaf()) { auto const& actualKey = - static_cast(newNode.get())->peekItem()->key(); + safe_downcast(newNode.get())->peekItem()->key(); // Validate that this leaf belongs at the target position auto const expectedNodeID = SHAMapNodeID::createID(node.getDepth(), actualKey); @@ -609,7 +615,7 @@ SHAMap::addKnownNode(SHAMapNodeID const& node, Slice const& rawNode, SHAMapSyncF newNode = prevNode->canonicalizeChild(branch, std::move(newNode)); - if (filter) + if (filter != nullptr) { Serializer s; newNode->serializeWithPrefix(s); @@ -637,12 +643,12 @@ SHAMap::deepCompare(SHAMap& other) const auto const [node, otherNode] = stack.top(); stack.pop(); - if (!node || !otherNode) + if ((node == nullptr) || (otherNode == nullptr)) { JLOG(journal_.info()) << "unable to fetch node"; return false; } - else if (otherNode->getHash() != node->getHash()) + if (otherNode->getHash() != node->getHash()) { JLOG(journal_.warn()) << "node hash mismatch"; return false; @@ -652,8 +658,8 @@ SHAMap::deepCompare(SHAMap& other) const { if (!otherNode->isLeaf()) return false; - auto& nodePeek = static_cast(node)->peekItem(); - auto& otherNodePeek = static_cast(otherNode)->peekItem(); + auto& nodePeek = safe_downcast(node)->peekItem(); + auto& otherNodePeek = safe_downcast(otherNode)->peekItem(); if (nodePeek->key() != otherNodePeek->key()) return false; if (nodePeek->slice() != otherNodePeek->slice()) @@ -663,8 +669,8 @@ SHAMap::deepCompare(SHAMap& other) const { if (!otherNode->isInner()) return false; - auto node_inner = static_cast(node); - auto other_inner = static_cast(otherNode); + auto node_inner = safe_downcast(node); + auto other_inner = safe_downcast(otherNode); for (int i = 0; i < 16; ++i) { if (node_inner->isEmptyBranch(i)) @@ -679,7 +685,7 @@ SHAMap::deepCompare(SHAMap& other) const auto next = descend(node_inner, i); auto otherNext = other.descend(other_inner, i); - if (!next || !otherNext) + if ((next == nullptr) || (otherNext == nullptr)) { JLOG(journal_.warn()) << "unable to fetch inner node"; return false; @@ -703,8 +709,8 @@ SHAMap::hasInnerNode(SHAMapNodeID const& targetNodeID, SHAMapHash const& targetN while (node->isInner() && (nodeID.getDepth() < targetNodeID.getDepth())) { - int branch = selectBranch(nodeID, targetNodeID.getNodeID()); - auto inner = static_cast(node); + int const branch = selectBranch(nodeID, targetNodeID.getNodeID()); + auto inner = safe_downcast(node); if (inner->isEmptyBranch(branch)) return false; @@ -728,8 +734,8 @@ SHAMap::hasLeafNode(uint256 const& tag, SHAMapHash const& targetNodeHash) const do { - int branch = selectBranch(nodeID, tag); - auto inner = static_cast(node); + int const branch = selectBranch(nodeID, tag); + auto inner = safe_downcast(node); if (inner->isEmptyBranch(branch)) return false; // Dead end, node must not be here @@ -800,7 +806,7 @@ SHAMap::verifyProofPath(uint256 const& rootHash, uint256 const& key, std::vector if (node->isInner()) { auto nodeId = SHAMapNodeID::createID(depth, key); - hash = static_cast(node.get()) + hash = safe_downcast(node.get()) ->getChildHash(selectBranch(nodeId, key)); } else diff --git a/src/libxrpl/tx/ApplyContext.cpp b/src/libxrpl/tx/ApplyContext.cpp index f62c63d1e6..d503643662 100644 --- a/src/libxrpl/tx/ApplyContext.cpp +++ b/src/libxrpl/tx/ApplyContext.cpp @@ -40,7 +40,7 @@ ApplyContext::discard() std::optional ApplyContext::apply(TER ter) { - return view_->apply(base_, tx, ter, parentBatchId_, flags_ & tapDRY_RUN, journal); + return view_->apply(base_, tx, ter, parentBatchId_, (flags_ & tapDRY_RUN) != 0u, journal); } std::size_t @@ -98,7 +98,7 @@ ApplyContext::checkInvariantsHelper( // short-circuits). While the logic is still correct, the log // message won't be. Every failed invariant should write to the log, // not just the first one. - std::array finalizers{ + std::array const finalizers{ {std::get(checkers).finalize(tx, result, fee, *view_, journal)...}}; // call each check's finalizer to see that it passes diff --git a/src/libxrpl/tx/SignerEntries.cpp b/src/libxrpl/tx/SignerEntries.cpp index aca1f2c19a..75659a7f04 100644 --- a/src/libxrpl/tx/SignerEntries.cpp +++ b/src/libxrpl/tx/SignerEntries.cpp @@ -12,8 +12,6 @@ namespace xrpl { Expected, NotTEC> SignerEntries::deserialize(STObject const& obj, beast::Journal journal, std::string_view annotation) { - std::pair, NotTEC> s; - if (!obj.isFieldPresent(sfSignerEntries)) { JLOG(journal.trace()) << "Malformed " << annotation << ": Need signer entry array."; diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 3ddac350e8..57d2207336 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -1,9 +1,13 @@ -#include #include #include #include -#include #include +#include +#include +#include +#include +#include +#include #include #include #include @@ -14,8 +18,6 @@ #include #include #include -#include -#include namespace xrpl { @@ -32,7 +34,7 @@ preflight0(PreflightContext const& ctx, std::uint32_t flagMask) if (!isPseudoTx(ctx.tx) || ctx.tx.isFieldPresent(sfNetworkID)) { - uint32_t nodeNID = ctx.registry.getNetworkIDService().getNetworkID(); + uint32_t const nodeNID = ctx.registry.get().getNetworkIDService().getNetworkID(); std::optional txNID = ctx.tx[~sfNetworkID]; if (nodeNID <= 1024) @@ -62,7 +64,7 @@ preflight0(PreflightContext const& ctx, std::uint32_t flagMask) return temINVALID; } - if (ctx.tx.getFlags() & flagMask) + if ((ctx.tx.getFlags() & flagMask) != 0u) { JLOG(ctx.j.debug()) << ctx.tx.peekAtField(sfTransactionType).getFullText() << ": invalid flags."; @@ -93,7 +95,7 @@ preflightCheckSigningKey(STObject const& sigObject, beast::Journal j) std::optional preflightCheckSimulateKeys(ApplyFlags flags, STObject const& sigObject, beast::Journal j) { - if (flags & tapDRY_RUN) // simulation + if ((flags & tapDRY_RUN) != 0u) // simulation { std::optional const signature = sigObject[~sfTxnSignature]; if (signature && !signature->empty()) @@ -191,9 +193,11 @@ NotTEC Transactor::preflight2(PreflightContext const& ctx) { if (auto const ret = detail::preflightCheckSimulateKeys(ctx.flags, ctx.tx, ctx.j)) + { // Skips following checks if the transaction is being simulated, // regardless of success or failure return *ret; + } // It should be impossible for the InnerBatchTxn flag to be set without // featureBatch being enabled @@ -207,7 +211,7 @@ Transactor::preflight2(PreflightContext const& ctx) // Do not add any checks after this point that are relevant for // batch inner transactions. They will be skipped. - auto const sigValid = checkValidity(ctx.registry.getHashRouter(), ctx.tx, ctx.rules); + auto const sigValid = checkValidity(ctx.registry.get().getHashRouter(), ctx.tx, ctx.rules); if (sigValid.first == Validity::SigBad) { // LCOV_EXCL_START JLOG(ctx.j.debug()) << "preflight2: bad signature. " << sigValid.second; @@ -297,7 +301,7 @@ Transactor::calculateOwnerReserveFee(ReadView const& view, STTx const& tx) // need to rethink charging an owner reserve as a transaction fee. // TODO: This function is static, and I don't want to add more parameters. // When it is finally refactored to be in a context that has access to the - // Application, include "app().overlay().networkID() > 2 ||" in the + // Application, include "app().getOverlay().networkID() > 2 ||" in the // condition. XRPL_ASSERT( view.fees().increment > view.fees().base * 100, @@ -313,7 +317,7 @@ Transactor::minimumFee( Fees const& fees, ApplyFlags flags) { - return scaleFeeLoad(baseFee, registry.getFeeTrack(), fees, flags & tapUNLIMITED); + return scaleFeeLoad(baseFee, registry.getFeeTrack(), fees, (flags & tapUNLIMITED) != 0u); } TER @@ -324,7 +328,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) auto const feePaid = ctx.tx[sfFee].xrp(); - if (ctx.flags & tapBATCH) + if ((ctx.flags & tapBATCH) != 0u) { if (feePaid == beast::zero) return tesSUCCESS; @@ -352,14 +356,20 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) if (feePaid == beast::zero) return tesSUCCESS; - auto const id = ctx.tx.isFieldPresent(sfDelegate) ? ctx.tx.getAccountID(sfDelegate) - : ctx.tx.getAccountID(sfAccount); + auto const id = ctx.tx.getFeePayer(); auto const sle = ctx.view.read(keylet::account(id)); if (!sle) return terNO_ACCOUNT; auto const balance = (*sle)[sfBalance].xrp(); + // NOTE: Because preclaim evaluates against a static readview, it + // does not reflect fee deductions from other transactions paid by + // the same account within the current ledger. + // As a result, if an account's balance is over-committed across multiple + // transactions, this check may pass optimistically. + // The fee shortfall will be handled by the Transactor::reset mechanism, + // which caps the fee to the remaining actual balance. if (balance < feePaid) { JLOG(ctx.j.trace()) << "Insufficient balance:" << " balance=" << to_string(balance) @@ -382,32 +392,18 @@ Transactor::payFee() { auto const feePaid = ctx_.tx[sfFee].xrp(); - if (ctx_.tx.isFieldPresent(sfDelegate)) - { - // Delegated transactions are paid by the delegated account. - auto const delegate = ctx_.tx.getAccountID(sfDelegate); - auto const delegatedSle = view().peek(keylet::account(delegate)); - if (!delegatedSle) - return tefINTERNAL; // LCOV_EXCL_LINE + auto const feePayer = ctx_.tx.getFeePayer(); + auto const sle = view().peek(keylet::account(feePayer)); + if (!sle) + return tefINTERNAL; // LCOV_EXCL_LINE - delegatedSle->setFieldAmount(sfBalance, delegatedSle->getFieldAmount(sfBalance) - feePaid); - view().update(delegatedSle); - } - else - { - auto const sle = view().peek(keylet::account(account_)); - if (!sle) - return tefINTERNAL; // LCOV_EXCL_LINE - - // Deduct the fee, so it's not available during the transaction. - // Will only write the account back if the transaction succeeds. - - mSourceBalance -= feePaid; - sle->setFieldAmount(sfBalance, mSourceBalance); - - // VFALCO Should we call view().rawDestroyXRP() here as well? - } + // Deduct the fee, so it's not available during the transaction. + // Will only write the account back if the transaction succeeds. + sle->setFieldAmount(sfBalance, sle->getFieldAmount(sfBalance) - feePaid); + if (feePayer != account_) + view().update(sle); // done in `apply()` for the account + // VFALCO Should we call view().rawDestroyXRP() here as well? return tesSUCCESS; } @@ -562,9 +558,13 @@ Transactor::ticketDelete( if (auto ticketCount = (*sleAccount)[~sfTicketCount]) { if (*ticketCount == 1) + { sleAccount->makeFieldAbsent(sfTicketCount); + } else + { ticketCount = *ticketCount - 1; + } } else { @@ -606,15 +606,14 @@ Transactor::apply() if (sle) { - mPriorBalance = STAmount{(*sle)[sfBalance]}.xrp(); - mSourceBalance = mPriorBalance; + preFeeBalance_ = STAmount{(*sle)[sfBalance]}.xrp(); TER result = consumeSeqProxy(sle); - if (result != tesSUCCESS) + if (!isTesSuccess(result)) return result; result = payFee(); - if (result != tesSUCCESS) + if (!isTesSuccess(result)) return result; if (sle->isFieldPresent(sfAccountTxnID)) @@ -639,10 +638,12 @@ Transactor::checkSign( auto const sle = view.read(keylet::account(idAccount)); if (view.rules().enabled(featureLendingProtocol) && isPseudoAccount(sle)) + { // Pseudo-accounts can't sign transactions. This check is gated on // the Lending Protocol amendment because that's the project it was // added under, and it doesn't justify another amendment return tefBAD_AUTH; + } } auto const pkSigner = sigObject.getFieldVL(sfSigningPubKey); @@ -658,7 +659,7 @@ Transactor::checkSign( return tesSUCCESS; } - if ((flags & tapDRY_RUN) && pkSigner.empty() && !sigObject.isFieldPresent(sfSigners)) + if (((flags & tapDRY_RUN) != 0u) && pkSigner.empty() && !sigObject.isFieldPresent(sfSigners)) { // simulate: skip signature validation when neither SigningPubKey nor // Signers are provided @@ -682,8 +683,7 @@ Transactor::checkSign( } // Look up the account. - auto const idSigner = - pkSigner.empty() ? idAccount : calcAccountID(PublicKey(makeSlice(pkSigner))); + auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner))); auto const sleAccount = view.read(keylet::account(idAccount)); if (!sleAccount) return terNO_ACCOUNT; @@ -784,7 +784,7 @@ Transactor::checkMultiSign( beast::Journal const j) { // Get id's SignerList and Quorum. - std::shared_ptr sleAccountSigners = view.read(keylet::signers(id)); + std::shared_ptr const sleAccountSigners = view.read(keylet::signers(id)); // If the signer list doesn't exist the account is not multi-signing. if (!sleAccountSigners) { @@ -891,7 +891,7 @@ Transactor::checkMultiSign( // Master Key. Account may not have asfDisableMaster set. std::uint32_t const signerAccountFlags = sleTxSignerRoot->getFieldU32(sfFlags); - if (signerAccountFlags & lsfDisableMaster) + if ((signerAccountFlags & lsfDisableMaster) != 0u) { JLOG(j.trace()) << "applyTransaction: Signer:Account lsfDisableMaster."; return tefMASTER_DISABLED; @@ -999,7 +999,7 @@ removeDeletedTrustLines( for (auto const& index : trustLines) { if (auto const sleState = view.peek({ltRIPPLE_STATE, index}); - deleteAMMTrustLine(view, sleState, std::nullopt, viewJ) != tesSUCCESS) + !isTesSuccess(deleteAMMTrustLine(view, sleState, std::nullopt, viewJ))) { JLOG(viewJ.error()) << "removeDeletedTrustLines: failed to delete AMM trustline"; } @@ -1023,9 +1023,7 @@ Transactor::reset(XRPAmount fee) if (!txnAcct) return {tefINTERNAL, beast::zero}; - auto const payerSle = ctx_.tx.isFieldPresent(sfDelegate) - ? view().peek(keylet::account(ctx_.tx.getAccountID(sfDelegate))) - : txnAcct; + auto const payerSle = view().peek(keylet::account(ctx_.tx.getFeePayer())); if (!payerSle) return {tefINTERNAL, beast::zero}; // LCOV_EXCL_LINE @@ -1082,15 +1080,15 @@ Transactor::operator()() // // raii classes for the current ledger rules. // fixUniversalNumber predate the rulesGuard and should be replaced. - NumberSO stNumberSO{view().rules().enabled(fixUniversalNumber)}; - CurrentTransactionRulesGuard currentTransactionRulesGuard(view().rules()); + NumberSO const stNumberSO{view().rules().enabled(fixUniversalNumber)}; + CurrentTransactionRulesGuard const currentTransactionRulesGuard(view().rules()); #ifdef DEBUG { Serializer ser; ctx_.tx.add(ser); SerialIter sit(ser.slice()); - STTx s2(sit); + STTx const s2(sit); if (!s2.isEquivalent(ctx_.tx)) { @@ -1104,13 +1102,14 @@ Transactor::operator()() } #endif - if (auto const& trap = ctx_.registry.trapTxID(); trap && *trap == ctx_.tx.getTransactionID()) + if (auto const& trap = ctx_.registry.get().getTrapTxID(); + trap && *trap == ctx_.tx.getTransactionID()) { trapTransaction(*trap); } auto result = ctx_.preclaimResult; - if (result == tesSUCCESS) + if (isTesSuccess(result)) result = apply(); // No transaction can return temUNKNOWN from apply, @@ -1126,7 +1125,7 @@ Transactor::operator()() if (ctx_.size() > oversizeMetaDataCap) result = tecOVERSIZE; - if (isTecClaim(result) && (view().flags() & tapFAIL_HARD)) + if (isTecClaim(result) && ((view().flags() & tapFAIL_HARD) != 0u)) { // If the tapFAIL_HARD flag is set, a tec result // must not do anything @@ -1206,16 +1205,27 @@ Transactor::operator()() // If necessary, remove any offers found unfunded during processing if ((result == tecOVERSIZE) || (result == tecKILLED)) - removeUnfundedOffers(view(), removedOffers, ctx_.registry.journal("View")); + { + removeUnfundedOffers(view(), removedOffers, ctx_.registry.get().getJournal("View")); + } if (result == tecEXPIRED) - removeExpiredNFTokenOffers(view(), expiredNFTokenOffers, ctx_.registry.journal("View")); + { + removeExpiredNFTokenOffers( + view(), expiredNFTokenOffers, ctx_.registry.get().getJournal("View")); + } if (result == tecINCOMPLETE) - removeDeletedTrustLines(view(), removedTrustLines, ctx_.registry.journal("View")); + { + removeDeletedTrustLines( + view(), removedTrustLines, ctx_.registry.get().getJournal("View")); + } if (result == tecEXPIRED) - removeExpiredCredentials(view(), expiredCredentials, ctx_.registry.journal("View")); + { + removeExpiredCredentials( + view(), expiredCredentials, ctx_.registry.get().getJournal("View")); + } applied = isTecClaim(result); } @@ -1271,7 +1281,7 @@ Transactor::operator()() metadata = ctx_.apply(result); } - if (ctx_.flags() & tapDRY_RUN) + if ((ctx_.flags() & tapDRY_RUN) != 0u) { applied = false; } diff --git a/src/libxrpl/tx/apply.cpp b/src/libxrpl/tx/apply.cpp index def82aca18..c8016002c2 100644 --- a/src/libxrpl/tx/apply.cpp +++ b/src/libxrpl/tx/apply.cpp @@ -50,8 +50,10 @@ checkValidity(HashRouter& router, STTx const& tx, Rules const& rules) } if (any(flags & SF_SIGBAD)) + { // Signature is known bad return {Validity::SigBad, "Transaction has bad signature."}; + } if (!any(flags & SF_SIGGOOD)) { @@ -66,14 +68,18 @@ checkValidity(HashRouter& router, STTx const& tx, Rules const& rules) // Signature is now known good if (any(flags & SF_LOCALBAD)) + { // ...but the local checks // are known bad. return {Validity::SigGoodOnly, "Local checks failed."}; + } if (any(flags & SF_LOCALGOOD)) + { // ...and the local checks // are known good. return {Validity::Valid, ""}; + } // Do the local checks std::string reason; @@ -110,7 +116,7 @@ template ApplyResult apply(ServiceRegistry& registry, OpenView& view, PreflightChecks&& preflightChecks) { - NumberSO stNumberSO{view.rules().enabled(fixUniversalNumber)}; + NumberSO const stNumberSO{view.rules().enabled(fixUniversalNumber)}; return doApply(preclaim(preflightChecks(), registry, view), registry, view); } @@ -143,13 +149,13 @@ applyBatchTransactions( beast::Journal j) { XRPL_ASSERT( - batchTxn.getTxnType() == ttBATCH && batchTxn.getFieldArray(sfRawTransactions).size() != 0, + batchTxn.getTxnType() == ttBATCH && !batchTxn.getFieldArray(sfRawTransactions).empty(), "Batch transaction missing sfRawTransactions"); auto const parentBatchId = batchTxn.getTransactionID(); auto const mode = batchTxn.getFlags(); - auto applyOneTransaction = [®istry, &j, &parentBatchId, &batchView](STTx&& tx) { + auto applyOneTransaction = [®istry, &j, &parentBatchId, &batchView](STTx const& tx) { OpenView perTxBatchView(batch_view, batchView); auto const ret = apply(registry, perTxBatchView, parentBatchId, tx, tapBATCH, j); @@ -182,14 +188,16 @@ applyBatchTransactions( if (!isTesSuccess(result.ter)) { - if (mode & tfAllOrNothing) + if ((mode & tfAllOrNothing) != 0u) return false; - if (mode & tfUntilFailure) + if ((mode & tfUntilFailure) != 0u) break; } - else if (mode & tfOnlyOne) + else if ((mode & tfOnlyOne) != 0u) + { break; + } } return applied != 0; diff --git a/src/libxrpl/tx/applySteps.cpp b/src/libxrpl/tx/applySteps.cpp index ad4a185078..b688377541 100644 --- a/src/libxrpl/tx/applySteps.cpp +++ b/src/libxrpl/tx/applySteps.cpp @@ -95,34 +95,31 @@ with_txn_type(Rules const& rules, TxType txnType, F&& f) // For Transactor::Normal // -// clang-format off -// Current formatter for rippled is based on clang-10, which does not handle `requires` clauses template -requires(T::ConsequencesFactory == Transactor::Normal) + requires(T::ConsequencesFactory == Transactor::Normal) TxConsequences - consequences_helper(PreflightContext const& ctx) +consequences_helper(PreflightContext const& ctx) { return TxConsequences(ctx.tx); }; // For Transactor::Blocker template -requires(T::ConsequencesFactory == Transactor::Blocker) + requires(T::ConsequencesFactory == Transactor::Blocker) TxConsequences - consequences_helper(PreflightContext const& ctx) +consequences_helper(PreflightContext const& ctx) { return TxConsequences(ctx.tx, TxConsequences::blocker); }; // For Transactor::Custom template -requires(T::ConsequencesFactory == Transactor::Custom) + requires(T::ConsequencesFactory == Transactor::Custom) TxConsequences - consequences_helper(PreflightContext const& ctx) +consequences_helper(PreflightContext const& ctx) { return T::makeTxConsequences(ctx); }; -// clang-format on static std::pair invoke_preflight(PreflightContext const& ctx) @@ -344,6 +341,7 @@ preclaim(PreflightResult const& preflightResult, ServiceRegistry& registry, Open { auto secondFlight = [&]() { if (preflightResult.parentBatchId) + { return preflight( registry, view.rules(), @@ -351,6 +349,7 @@ preclaim(PreflightResult const& preflightResult, ServiceRegistry& registry, Open preflightResult.tx, preflightResult.flags, preflightResult.j); + } return preflight( registry, @@ -383,7 +382,7 @@ preclaim(PreflightResult const& preflightResult, ServiceRegistry& registry, Open try { - if (ctx->preflightResult != tesSUCCESS) + if (!isTesSuccess(ctx->preflightResult)) return {*ctx, ctx->preflightResult}; return {*ctx, invoke_preclaim(*ctx)}; } diff --git a/src/libxrpl/tx/invariants/AMMInvariant.cpp b/src/libxrpl/tx/invariants/AMMInvariant.cpp index 60d05f298c..0be4bedc07 100644 --- a/src/libxrpl/tx/invariants/AMMInvariant.cpp +++ b/src/libxrpl/tx/invariants/AMMInvariant.cpp @@ -2,9 +2,9 @@ // #include #include +#include +#include #include -#include -#include namespace xrpl { @@ -28,7 +28,7 @@ ValidAMM::visitEntry( } // AMM pool changed else if ( - (type == ltRIPPLE_STATE && after->getFlags() & lsfAMMNode) || + (type == ltRIPPLE_STATE && ((after->getFlags() & lsfAMMNode) != 0u)) || (type == ltACCOUNT_ROOT && after->isFieldPresent(sfAMMID))) { ammPoolChanged_ = true; @@ -55,8 +55,10 @@ validBalances( bool const positive = amount > beast::zero && amount2 > beast::zero && lptAMMBalance > beast::zero; if (zeroAllowed == ValidAMM::ZeroAllowed::Yes) + { return positive || (amount == beast::zero && amount2 == beast::zero && lptAMMBalance == beast::zero); + } return positive; } @@ -152,7 +154,7 @@ ValidAMM::finalizeDelete(bool enforce, TER res, beast::Journal const& j) const if (ammAccount_) { // LCOV_EXCL_START - std::string const msg = (res == tesSUCCESS) ? "AMM object is not deleted on tesSUCCESS" + 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; if (enforce) @@ -236,7 +238,9 @@ ValidAMM::finalizeDeposit( // LCOV_EXCL_STOP } else if (!generalInvariant(tx, view, ZeroAllowed::No, j) && enforce) + { return false; + } return true; } @@ -271,7 +275,7 @@ ValidAMM::finalize( { // Delete may return tecINCOMPLETE if there are too many // trustlines to delete. - if (result != tesSUCCESS && result != tecINCOMPLETE) + if (!isTesSuccess(result) && result != tecINCOMPLETE) return true; bool const enforce = view.rules().enabled(fixAMMv1_3); diff --git a/src/libxrpl/tx/invariants/FreezeInvariant.cpp b/src/libxrpl/tx/invariants/FreezeInvariant.cpp index 858c4cdcb8..ee82157d44 100644 --- a/src/libxrpl/tx/invariants/FreezeInvariant.cpp +++ b/src/libxrpl/tx/invariants/FreezeInvariant.cpp @@ -68,7 +68,7 @@ TransfersNotFrozen::finalize( { auto const issuerSle = findIssuer(issue.account, view); // It should be impossible for the issuer to not be found, but check - // just in case so rippled doesn't crash in release. + // just in case so xrpld doesn't crash in release. if (!issuerSle) { // The comment above starting with "assert(enforce)" explains this @@ -127,7 +127,7 @@ TransfersNotFrozen::calculateBalanceChange( bool isDelete) { auto const getBalance = [](auto const& line, auto const& other, bool zero) { - STAmount amt = line ? line->at(sfBalance) : other->at(sfBalance).zeroed(); + STAmount const amt = line ? line->at(sfBalance) : other->at(sfBalance).zeroed(); return zero ? amt.zeroed() : amt; }; @@ -157,9 +157,13 @@ TransfersNotFrozen::recordBalance(Issue const& issue, BalanceChange change) "balance sign."); auto& changes = balanceChanges_[issue]; if (change.balanceChangeSign < 0) + { changes.senders.emplace_back(std::move(change)); + } else + { changes.receivers.emplace_back(std::move(change)); + } } void @@ -267,12 +271,7 @@ TransfersNotFrozen::validateFrozenState( "xrpl::TransfersNotFrozen::validateFrozenState : enforce " "invariant."); - if (enforce) - { - return false; - } - - return true; + return !enforce; } } // namespace xrpl diff --git a/src/libxrpl/tx/invariants/InvariantCheck.cpp b/src/libxrpl/tx/invariants/InvariantCheck.cpp index 025d2d5fec..bf0f65f000 100644 --- a/src/libxrpl/tx/invariants/InvariantCheck.cpp +++ b/src/libxrpl/tx/invariants/InvariantCheck.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -147,7 +149,7 @@ XRPNotCreated::finalize( TER const, XRPAmount const fee, ReadView const&, - beast::Journal const& j) + beast::Journal const& j) const { // The net change should never be positive, as this would mean that the // transaction created XRP out of thin air. That's not possible. @@ -207,7 +209,7 @@ XRPBalanceChecks::finalize( TER const, XRPAmount const, ReadView const&, - beast::Journal const& j) + beast::Journal const& j) const { if (bad_) { @@ -251,7 +253,7 @@ NoBadOffers::finalize( TER const, XRPAmount const, ReadView const&, - beast::Journal const& j) + beast::Journal const& j) const { if (bad_) { @@ -344,7 +346,7 @@ NoZeroEscrow::finalize( TER const, XRPAmount const, ReadView const& rv, - beast::Journal const& j) + beast::Journal const& j) const { if (bad_) { @@ -373,20 +375,22 @@ AccountRootsNotDeleted::finalize( TER const result, XRPAmount const, ReadView const&, - beast::Journal const& j) + beast::Journal const& j) const { // AMM account root can be deleted as the result of AMM withdraw/delete // transaction when the total AMM LP Tokens balance goes to 0. // A successful AccountDelete or AMMDelete MUST delete exactly // one account root. - if (hasPrivilege(tx, mustDeleteAcct) && result == tesSUCCESS) + if (hasPrivilege(tx, mustDeleteAcct) && isTesSuccess(result)) { if (accountsDeleted_ == 1) return true; if (accountsDeleted_ == 0) + { JLOG(j.fatal()) << "Invariant failed: account deletion " "succeeded without deleting an account"; + } else JLOG(j.fatal()) << "Invariant failed: account deletion " "succeeded but deleted multiple accounts!"; @@ -396,7 +400,7 @@ AccountRootsNotDeleted::finalize( // A successful AMMWithdraw/AMMClawback MAY delete one account root // when the total AMM LP Tokens balance goes to 0. Not every AMM withdraw // deletes the AMM account, accountsDeleted_ is set if it is deleted. - if (hasPrivilege(tx, mayDeleteAcct) && result == tesSUCCESS && accountsDeleted_ == 1) + if (hasPrivilege(tx, mayDeleteAcct) && isTesSuccess(result) && accountsDeleted_ == 1) return true; if (accountsDeleted_ == 0) @@ -565,7 +569,7 @@ LedgerEntryTypesMatch::finalize( TER const, XRPAmount const, ReadView const&, - beast::Journal const& j) + beast::Journal const& j) const { if ((!typeMismatch_) && (!invalidTypeAdded_)) return true; @@ -607,7 +611,7 @@ NoXRPTrustLines::finalize( TER const, XRPAmount const, ReadView const&, - beast::Journal const& j) + beast::Journal const& j) const { if (!xrpTrustLine_) return true; @@ -627,11 +631,11 @@ NoDeepFreezeTrustLinesWithoutFreeze::visitEntry( if (after && after->getType() == ltRIPPLE_STATE) { std::uint32_t const uFlags = after->getFieldU32(sfFlags); - bool const lowFreeze = uFlags & lsfLowFreeze; - bool const lowDeepFreeze = uFlags & lsfLowDeepFreeze; + bool const lowFreeze = (uFlags & lsfLowFreeze) != 0u; + bool const lowDeepFreeze = (uFlags & lsfLowDeepFreeze) != 0u; - bool const highFreeze = uFlags & lsfHighFreeze; - bool const highDeepFreeze = uFlags & lsfHighDeepFreeze; + bool const highFreeze = (uFlags & lsfHighFreeze) != 0u; + bool const highDeepFreeze = (uFlags & lsfHighDeepFreeze) != 0u; deepFreezeWithoutFreeze_ = (lowDeepFreeze && !lowFreeze) || (highDeepFreeze && !highFreeze); } @@ -643,7 +647,7 @@ NoDeepFreezeTrustLinesWithoutFreeze::finalize( TER const, XRPAmount const, ReadView const&, - beast::Journal const& j) + beast::Journal const& j) const { if (!deepFreezeWithoutFreeze_) return true; @@ -676,7 +680,7 @@ ValidNewAccountRoot::finalize( TER const result, XRPAmount const, ReadView const& view, - beast::Journal const& j) + beast::Journal const& j) const { if (accountsCreated_ == 0) return true; @@ -689,7 +693,7 @@ ValidNewAccountRoot::finalize( } // From this point on we know exactly one account was created. - if (hasPrivilege(tx, createAcct | createPseudoAcct) && result == tesSUCCESS) + if (hasPrivilege(tx, createAcct | createPseudoAcct) && isTesSuccess(result)) { bool const pseudoAccount = (pseudoAccount_ && @@ -751,12 +755,12 @@ ValidClawback::finalize( TER const result, XRPAmount const, ReadView const& view, - beast::Journal const& j) + beast::Journal const& j) const { if (tx.getTxnType() != ttCLAWBACK) return true; - if (result == tesSUCCESS) + if (isTesSuccess(result)) { if (trustlinesChanged > 1) { @@ -814,8 +818,10 @@ ValidPseudoAccounts::visitEntry( std::shared_ptr const& after) { if (isDelete) + { // Deletion is ignored return; + } if (after && after->getType() == ltACCOUNT_ROOT) { @@ -903,8 +909,10 @@ NoModifiedUnmodifiableFields::visitEntry( std::shared_ptr const& after) { if (isDelete || !before) + { // Creation and deletion are ignored return; + } changedEntries_.emplace(before, after); } diff --git a/src/libxrpl/tx/invariants/LoanBrokerInvariant.cpp b/src/libxrpl/tx/invariants/LoanBrokerInvariant.cpp new file mode 100644 index 0000000000..8ee0a0deb8 --- /dev/null +++ b/src/libxrpl/tx/invariants/LoanBrokerInvariant.cpp @@ -0,0 +1,208 @@ +#include +// +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +void +ValidLoanBroker::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + if (after) + { + if (after->getType() == ltLOAN_BROKER) + { + auto& broker = brokers_[after->key()]; + broker.brokerBefore = before; + broker.brokerAfter = after; + } + else if (after->getType() == ltACCOUNT_ROOT && after->isFieldPresent(sfLoanBrokerID)) + { + auto const& loanBrokerID = after->at(sfLoanBrokerID); + // create an entry if one doesn't already exist + brokers_.emplace(loanBrokerID, BrokerInfo{}); + } + else if (after->getType() == ltRIPPLE_STATE) + { + lines_.emplace_back(after); + } + else if (after->getType() == ltMPTOKEN) + { + mpts_.emplace_back(after); + } + } +} + +bool +ValidLoanBroker::goodZeroDirectory( + ReadView const& view, + SLE::const_ref dir, + beast::Journal const& j) +{ + auto const next = dir->at(~sfIndexNext); + auto const prev = dir->at(~sfIndexPrevious); + if ((prev && (*prev != 0u)) || (next && (*next != 0u))) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker with zero " + "OwnerCount has multiple directory pages"; + return false; + } + auto indexes = dir->getFieldV256(sfIndexes); + if (indexes.size() > 1) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker with zero " + "OwnerCount has multiple indexes in the Directory root"; + return false; + } + if (indexes.size() == 1) + { + auto const index = indexes.value().front(); + auto const sle = view.read(keylet::unchecked(index)); + if (!sle) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker directory corrupt"; + return false; + } + if (sle->getType() != ltRIPPLE_STATE && sle->getType() != ltMPTOKEN) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker with zero " + "OwnerCount has an unexpected entry in the directory"; + return false; + } + } + + return true; +} + +bool +ValidLoanBroker::finalize( + STTx const& tx, + TER const, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) +{ + // Loan Brokers will not exist on ledger if the Lending Protocol amendment + // is not enabled, so there's no need to check it. + + for (auto const& line : lines_) + { + for (auto const& field : {&sfLowLimit, &sfHighLimit}) + { + auto const account = view.read(keylet::account(line->at(*field).getIssuer())); + // This Invariant doesn't know about the rules for Trust Lines, so + // if the account is missing, don't treat it as an error. This + // loop is only concerned with finding Broker pseudo-accounts + if (account && account->isFieldPresent(sfLoanBrokerID)) + { + auto const& loanBrokerID = account->at(sfLoanBrokerID); + // create an entry if one doesn't already exist + brokers_.emplace(loanBrokerID, BrokerInfo{}); + } + } + } + for (auto const& mpt : mpts_) + { + auto const account = view.read(keylet::account(mpt->at(sfAccount))); + // This Invariant doesn't know about the rules for MPTokens, so + // if the account is missing, don't treat is as an error. This + // loop is only concerned with finding Broker pseudo-accounts + if (account && account->isFieldPresent(sfLoanBrokerID)) + { + auto const& loanBrokerID = account->at(sfLoanBrokerID); + // create an entry if one doesn't already exist + brokers_.emplace(loanBrokerID, BrokerInfo{}); + } + } + + for (auto const& [brokerID, broker] : brokers_) + { + auto const& after = + broker.brokerAfter ? broker.brokerAfter : view.read(keylet::loanbroker(brokerID)); + + if (!after) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker missing"; + return false; + } + + auto const& before = broker.brokerBefore; + + // https://github.com/Tapanito/XRPL-Standards/blob/xls-66-lending-protocol/XLS-0066d-lending-protocol/README.md#3123-invariants + // If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most + // one node (the root), which will only hold entries for `RippleState` + // or `MPToken` objects. + if (after->at(sfOwnerCount) == 0) + { + auto const dir = view.read(keylet::ownerDir(after->at(sfAccount))); + if (dir) + { + if (!goodZeroDirectory(view, dir, j)) + { + return false; + } + } + } + if (before && before->at(sfLoanSequence) > after->at(sfLoanSequence)) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker sequence number " + "decreased"; + return false; + } + if (after->at(sfDebtTotal) < 0) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker debt total is negative"; + return false; + } + if (after->at(sfCoverAvailable) < 0) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker cover available is negative"; + return false; + } + auto const vault = view.read(keylet::vault(after->at(sfVaultID))); + if (!vault) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker vault ID is invalid"; + return false; + } + auto const& vaultAsset = vault->at(sfAsset); + auto const pseudoBalance = accountHolds( + view, + after->at(sfAccount), + vaultAsset, + FreezeHandling::fhIGNORE_FREEZE, + AuthHandling::ahIGNORE_AUTH, + j); + if (after->at(sfCoverAvailable) < pseudoBalance) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker cover available " + "is less than pseudo-account asset balance"; + return false; + } + + if (view.rules().enabled(fixSecurity3_1_3)) + { + // Don't check the balance when LoanBroker is deleted, + // sfCoverAvailable is not zeroed + if (tx.getTxnType() != ttLOAN_BROKER_DELETE && + after->at(sfCoverAvailable) > pseudoBalance) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker cover available is greater " + "than pseudo-account asset balance"; + return false; + } + } + } + return true; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/invariants/LoanInvariant.cpp b/src/libxrpl/tx/invariants/LoanInvariant.cpp index 01c4da46ac..6ce1261612 100644 --- a/src/libxrpl/tx/invariants/LoanInvariant.cpp +++ b/src/libxrpl/tx/invariants/LoanInvariant.cpp @@ -2,196 +2,11 @@ // #include #include -#include -#include #include #include -#include namespace xrpl { -void -ValidLoanBroker::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) -{ - if (after) - { - if (after->getType() == ltLOAN_BROKER) - { - auto& broker = brokers_[after->key()]; - broker.brokerBefore = before; - broker.brokerAfter = after; - } - else if (after->getType() == ltACCOUNT_ROOT && after->isFieldPresent(sfLoanBrokerID)) - { - auto const& loanBrokerID = after->at(sfLoanBrokerID); - // create an entry if one doesn't already exist - brokers_.emplace(loanBrokerID, BrokerInfo{}); - } - else if (after->getType() == ltRIPPLE_STATE) - { - lines_.emplace_back(after); - } - else if (after->getType() == ltMPTOKEN) - { - mpts_.emplace_back(after); - } - } -} - -bool -ValidLoanBroker::goodZeroDirectory( - ReadView const& view, - SLE::const_ref dir, - beast::Journal const& j) const -{ - auto const next = dir->at(~sfIndexNext); - auto const prev = dir->at(~sfIndexPrevious); - if ((prev && *prev) || (next && *next)) - { - JLOG(j.fatal()) << "Invariant failed: Loan Broker with zero " - "OwnerCount has multiple directory pages"; - return false; - } - auto indexes = dir->getFieldV256(sfIndexes); - if (indexes.size() > 1) - { - JLOG(j.fatal()) << "Invariant failed: Loan Broker with zero " - "OwnerCount has multiple indexes in the Directory root"; - return false; - } - if (indexes.size() == 1) - { - auto const index = indexes.value().front(); - auto const sle = view.read(keylet::unchecked(index)); - if (!sle) - { - JLOG(j.fatal()) << "Invariant failed: Loan Broker directory corrupt"; - return false; - } - if (sle->getType() != ltRIPPLE_STATE && sle->getType() != ltMPTOKEN) - { - JLOG(j.fatal()) << "Invariant failed: Loan Broker with zero " - "OwnerCount has an unexpected entry in the directory"; - return false; - } - } - - return true; -} - -bool -ValidLoanBroker::finalize( - STTx const& tx, - TER const, - XRPAmount const, - ReadView const& view, - beast::Journal const& j) -{ - // Loan Brokers will not exist on ledger if the Lending Protocol amendment - // is not enabled, so there's no need to check it. - - for (auto const& line : lines_) - { - for (auto const& field : {&sfLowLimit, &sfHighLimit}) - { - auto const account = view.read(keylet::account(line->at(*field).getIssuer())); - // This Invariant doesn't know about the rules for Trust Lines, so - // if the account is missing, don't treat it as an error. This - // loop is only concerned with finding Broker pseudo-accounts - if (account && account->isFieldPresent(sfLoanBrokerID)) - { - auto const& loanBrokerID = account->at(sfLoanBrokerID); - // create an entry if one doesn't already exist - brokers_.emplace(loanBrokerID, BrokerInfo{}); - } - } - } - for (auto const& mpt : mpts_) - { - auto const account = view.read(keylet::account(mpt->at(sfAccount))); - // This Invariant doesn't know about the rules for MPTokens, so - // if the account is missing, don't treat is as an error. This - // loop is only concerned with finding Broker pseudo-accounts - if (account && account->isFieldPresent(sfLoanBrokerID)) - { - auto const& loanBrokerID = account->at(sfLoanBrokerID); - // create an entry if one doesn't already exist - brokers_.emplace(loanBrokerID, BrokerInfo{}); - } - } - - for (auto const& [brokerID, broker] : brokers_) - { - auto const& after = - broker.brokerAfter ? broker.brokerAfter : view.read(keylet::loanbroker(brokerID)); - - if (!after) - { - JLOG(j.fatal()) << "Invariant failed: Loan Broker missing"; - return false; - } - - auto const& before = broker.brokerBefore; - - // https://github.com/Tapanito/XRPL-Standards/blob/xls-66-lending-protocol/XLS-0066d-lending-protocol/README.md#3123-invariants - // If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most - // one node (the root), which will only hold entries for `RippleState` - // or `MPToken` objects. - if (after->at(sfOwnerCount) == 0) - { - auto const dir = view.read(keylet::ownerDir(after->at(sfAccount))); - if (dir) - { - if (!goodZeroDirectory(view, dir, j)) - { - return false; - } - } - } - if (before && before->at(sfLoanSequence) > after->at(sfLoanSequence)) - { - JLOG(j.fatal()) << "Invariant failed: Loan Broker sequence number " - "decreased"; - return false; - } - if (after->at(sfDebtTotal) < 0) - { - JLOG(j.fatal()) << "Invariant failed: Loan Broker debt total is negative"; - return false; - } - if (after->at(sfCoverAvailable) < 0) - { - JLOG(j.fatal()) << "Invariant failed: Loan Broker cover available is negative"; - return false; - } - auto const vault = view.read(keylet::vault(after->at(sfVaultID))); - if (!vault) - { - JLOG(j.fatal()) << "Invariant failed: Loan Broker vault ID is invalid"; - return false; - } - auto const& vaultAsset = vault->at(sfAsset); - if (after->at(sfCoverAvailable) < accountHolds( - view, - after->at(sfAccount), - vaultAsset, - FreezeHandling::fhIGNORE_FREEZE, - AuthHandling::ahIGNORE_AUTH, - j)) - { - JLOG(j.fatal()) << "Invariant failed: Loan Broker cover available " - "is less than pseudo-account asset balance"; - return false; - } - } - return true; -} - -//------------------------------------------------------------------------------ - void ValidLoan::visitEntry( bool isDelete, @@ -235,8 +50,7 @@ ValidLoan::finalize( after->at(sfPrincipalOutstanding) == beast::zero && after->at(sfManagementFeeOutstanding) == beast::zero) { - JLOG(j.fatal()) << "Invariant failed: Loan with zero payments " - "remaining has not been paid off"; + JLOG(j.fatal()) << "Invariant failed: Fully paid off Loan still has payments remaining"; return false; } if (before && (before->isFlag(lsfLoanOverpayment) != after->isFlag(lsfLoanOverpayment))) diff --git a/src/libxrpl/tx/invariants/MPTInvariant.cpp b/src/libxrpl/tx/invariants/MPTInvariant.cpp index 20957b8d43..7b76e70c86 100644 --- a/src/libxrpl/tx/invariants/MPTInvariant.cpp +++ b/src/libxrpl/tx/invariants/MPTInvariant.cpp @@ -19,15 +19,21 @@ ValidMPTIssuance::visitEntry( if (after && after->getType() == ltMPTOKEN_ISSUANCE) { if (isDelete) + { mptIssuancesDeleted_++; + } else if (!before) + { mptIssuancesCreated_++; + } } if (after && after->getType() == ltMPTOKEN) { if (isDelete) + { mptokensDeleted_++; + } else if (!before) { mptokensCreated_++; @@ -44,13 +50,13 @@ ValidMPTIssuance::finalize( TER const result, XRPAmount const _fee, ReadView const& view, - beast::Journal const& j) + beast::Journal const& j) const { - if (result == tesSUCCESS) + if (isTesSuccess(result)) { auto const& rules = view.rules(); [[maybe_unused]] - bool enforceCreatedByIssuer = + bool const enforceCreatedByIssuer = rules.enabled(featureSingleAssetVault) || rules.enabled(featureLendingProtocol); if (mptCreatedByIssuer_) { @@ -122,26 +128,25 @@ ValidMPTIssuance::finalize( "succeeded but created MPT issuances"; return false; } - else if (mptIssuancesDeleted_ > 0) + if (mptIssuancesDeleted_ > 0) { JLOG(j.fatal()) << "Invariant failed: MPT authorize " "succeeded but deleted issuances"; return false; } - else if (lendingProtocolEnabled && mptokensCreated_ + mptokensDeleted_ > 1) + if (lendingProtocolEnabled && mptokensCreated_ + mptokensDeleted_ > 1) { JLOG(j.fatal()) << "Invariant failed: MPT authorize succeeded " "but created/deleted bad number mptokens"; return false; } - else if (submittedByIssuer && (mptokensCreated_ > 0 || mptokensDeleted_ > 0)) + if (submittedByIssuer && (mptokensCreated_ > 0 || mptokensDeleted_ > 0)) { JLOG(j.fatal()) << "Invariant failed: MPT authorize submitted by issuer " "succeeded but created/deleted mptokens"; return false; } - else if ( - !submittedByIssuer && hasPrivilege(tx, mustAuthorizeMPT) && + if (!submittedByIssuer && hasPrivilege(tx, mustAuthorizeMPT) && (mptokensCreated_ + mptokensDeleted_ != 1)) { // if the holder submitted this tx, then a mptoken must be diff --git a/src/libxrpl/tx/invariants/NFTInvariant.cpp b/src/libxrpl/tx/invariants/NFTInvariant.cpp index 67f1e51231..e37e55e709 100644 --- a/src/libxrpl/tx/invariants/NFTInvariant.cpp +++ b/src/libxrpl/tx/invariants/NFTInvariant.cpp @@ -2,11 +2,12 @@ // #include #include +#include #include #include #include +#include #include -#include namespace xrpl { @@ -120,7 +121,7 @@ ValidNFTokenPage::finalize( TER const result, XRPAmount const, ReadView const& view, - beast::Journal const& j) + beast::Journal const& j) const { if (badLink_) { @@ -196,7 +197,7 @@ NFTokenCountTracking::finalize( TER const result, XRPAmount const, ReadView const& view, - beast::Journal const& j) + beast::Journal const& j) const { if (!hasPrivilege(tx, changeNFTCounts)) { @@ -219,14 +220,14 @@ NFTokenCountTracking::finalize( if (tx.getTxnType() == ttNFTOKEN_MINT) { - if (result == tesSUCCESS && beforeMintedTotal >= afterMintedTotal) + if (isTesSuccess(result) && beforeMintedTotal >= afterMintedTotal) { JLOG(j.fatal()) << "Invariant failed: successful minting didn't increase " "the number of minted tokens."; return false; } - if (result != tesSUCCESS && beforeMintedTotal != afterMintedTotal) + if (!isTesSuccess(result) && beforeMintedTotal != afterMintedTotal) { JLOG(j.fatal()) << "Invariant failed: failed minting changed the " "number of minted tokens."; @@ -243,7 +244,7 @@ NFTokenCountTracking::finalize( if (tx.getTxnType() == ttNFTOKEN_BURN) { - if (result == tesSUCCESS) + if (isTesSuccess(result)) { if (beforeBurnedTotal >= afterBurnedTotal) { @@ -253,7 +254,7 @@ NFTokenCountTracking::finalize( } } - if (result != tesSUCCESS && beforeBurnedTotal != afterBurnedTotal) + if (!isTesSuccess(result) && beforeBurnedTotal != afterBurnedTotal) { JLOG(j.fatal()) << "Invariant failed: failed burning changed the " "number of burned tokens."; diff --git a/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp b/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp index 2ece1f3fc0..e932a6ba09 100644 --- a/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp +++ b/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp @@ -24,9 +24,13 @@ ValidPermissionedDEX::visitEntry( if (after && after->getType() == ltOFFER) { if (after->isFieldPresent(sfDomainID)) + { domains_.insert(after->getFieldH256(sfDomainID)); + } else + { regularOffers_ = true; + } // if a hybrid offer is missing domain or additional book, there's // something wrong @@ -46,7 +50,7 @@ ValidPermissionedDEX::finalize( beast::Journal const& j) { auto const txType = tx.getTxnType(); - if ((txType != ttPAYMENT && txType != ttOFFER_CREATE) || result != tesSUCCESS) + if ((txType != ttPAYMENT && txType != ttOFFER_CREATE) || !isTesSuccess(result)) return true; // For each offercreate transaction, check if diff --git a/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp b/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp index 77acbe12c6..7365fc7b1a 100644 --- a/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp +++ b/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp @@ -1,7 +1,7 @@ #include // #include -#include +#include #include #include #include @@ -38,7 +38,7 @@ ValidPermissionedDomain::visitEntry( break; } } - sleStatus.emplace_back(std::move(ss)); + sleStatus.emplace_back(ss); }; if (after) @@ -89,10 +89,11 @@ ValidPermissionedDomain::finalize( if (view.rules().enabled(fixPermissionedDomainInvariant)) { // No permissioned domains should be affected if the transaction failed - if (result != tesSUCCESS) - // If nothing changed, all is good. If there were changes, that's - // bad. + if (!isTesSuccess(result)) + { + // If nothing changed, all is good. If there were changes, that's bad. return sleStatus_.empty(); + } if (sleStatus_.size() > 1) { @@ -152,7 +153,7 @@ ValidPermissionedDomain::finalize( } else { - if (tx.getTxnType() != ttPERMISSIONED_DOMAIN_SET || result != tesSUCCESS || + if (tx.getTxnType() != ttPERMISSIONED_DOMAIN_SET || !isTesSuccess(result) || sleStatus_.empty()) return true; return check(sleStatus_[0], j); diff --git a/src/libxrpl/tx/invariants/VaultInvariant.cpp b/src/libxrpl/tx/invariants/VaultInvariant.cpp index c3db3a563a..c6b3295569 100644 --- a/src/libxrpl/tx/invariants/VaultInvariant.cpp +++ b/src/libxrpl/tx/invariants/VaultInvariant.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -157,7 +158,7 @@ ValidVault::finalize( return true; // Not a vault operation } - else if (!(hasPrivilege(tx, mustModifyVault) || hasPrivilege(tx, mayModifyVault))) + if (!(hasPrivilege(tx, mustModifyVault) || hasPrivilege(tx, mayModifyVault))) { JLOG(j.fatal()) << // "Invariant failed: vault updated by a wrong transaction type"; @@ -204,7 +205,7 @@ ValidVault::finalize( for (auto const& e : beforeMPTs_) { if (e.share.getMptID() == beforeVault.shareMPTID) - return std::move(e); + return e; } return std::nullopt; }(); @@ -239,7 +240,7 @@ ValidVault::finalize( return result; } - else if (txnType == ttVAULT_DELETE) + if (txnType == ttVAULT_DELETE) { JLOG(j.fatal()) << "Invariant failed: vault deletion succeeded without " "deleting a vault"; @@ -373,7 +374,7 @@ ValidVault::finalize( for (auto const& e : beforeMPTs_) { if (e.share.getMptID() == beforeVault.shareMPTID) - return std::move(e); + return e; } return std::nullopt; }(); diff --git a/src/xrpld/app/paths/detail/AMMLiquidity.cpp b/src/libxrpl/tx/paths/AMMLiquidity.cpp similarity index 90% rename from src/xrpld/app/paths/detail/AMMLiquidity.cpp rename to src/libxrpl/tx/paths/AMMLiquidity.cpp index 3ffc86d1ba..bb7f229374 100644 --- a/src/xrpld/app/paths/detail/AMMLiquidity.cpp +++ b/src/libxrpl/tx/paths/AMMLiquidity.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include namespace xrpl { @@ -50,12 +50,10 @@ AMMLiquidity::generateFibSeqOffer(TAmounts const& balances if (ammContext_.curIters() == 0) return cur; - // clang-format off constexpr std::uint32_t fib[AMMContext::MaxIterations] = { - 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, - 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, - 196418, 317811, 514229, 832040, 1346269}; - // clang-format on + 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, + 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, + 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269}; XRPL_ASSERT( !ammContext_.maxItersReached(), @@ -80,11 +78,17 @@ constexpr T maxAmount() { if constexpr (std::is_same_v) + { return XRPAmount(STAmount::cMaxNative); + } else if constexpr (std::is_same_v) + { return IOUAmount(STAmount::cMaxValue / 2, STAmount::cMaxOffset); + } else if constexpr (std::is_same_v) + { return STAmount(STAmount::cMaxValue / 2, STAmount::cMaxOffset); + } } template @@ -108,14 +112,12 @@ AMMLiquidity::maxOffer(TAmounts const& balances, Rules con balances, Quality{balances}); } - else - { - auto const out = maxOut(balances.out, issueOut()); - if (out <= TOut{0} || out >= balances.out) - return std::nullopt; - return AMMOffer( - *this, {swapAssetOut(balances, out, tradingFee_), out}, balances, Quality{balances}); - } + + auto const out = maxOut(balances.out, issueOut()); + if (out <= TOut{0} || out >= balances.out) + return std::nullopt; + return AMMOffer( + *this, {swapAssetOut(balances, out, tradingFee_), out}, balances, Quality{balances}); } template @@ -166,7 +168,7 @@ AMMLiquidity::getOffer(ReadView const& view, std::optional c return std::nullopt; return AMMOffer(*this, amounts, balances, Quality{amounts}); } - else if (!clobQuality) + if (!clobQuality) { // If there is no CLOB to compare against, return the largest // amount, which doesn't overflow. The size is going to be @@ -175,13 +177,12 @@ AMMLiquidity::getOffer(ReadView const& view, std::optional c // nullopt if the pool is small. return maxOffer(balances, view.rules()); } - else if ( - auto const amounts = + if (auto const amounts = changeSpotPriceQuality(balances, *clobQuality, tradingFee_, view.rules(), j_)) { return AMMOffer(*this, *amounts, balances, Quality{*amounts}); } - else if (view.rules().enabled(fixAMMv1_2)) + if (view.rules().enabled(fixAMMv1_2)) { if (auto const maxAMMOffer = maxOffer(balances, view.rules()); maxAMMOffer && Quality{maxAMMOffer->amount()} > *clobQuality) @@ -192,9 +193,11 @@ AMMLiquidity::getOffer(ReadView const& view, std::optional c { JLOG(j_.error()) << "AMMLiquidity::getOffer overflow " << e.what(); if (!view.rules().enabled(fixAMMOverflowOffer)) + { return maxOffer(balances, view.rules()); - else - return std::nullopt; + } + + return std::nullopt; } catch (std::exception const& e) { diff --git a/src/xrpld/app/paths/detail/AMMOffer.cpp b/src/libxrpl/tx/paths/AMMOffer.cpp similarity index 95% rename from src/xrpld/app/paths/detail/AMMOffer.cpp rename to src/libxrpl/tx/paths/AMMOffer.cpp index 4871d2251a..79071850c2 100644 --- a/src/xrpld/app/paths/detail/AMMOffer.cpp +++ b/src/libxrpl/tx/paths/AMMOffer.cpp @@ -1,7 +1,6 @@ -#include -#include - #include +#include +#include namespace xrpl { @@ -11,11 +10,8 @@ AMMOffer::AMMOffer( TAmounts const& amounts, TAmounts const& balances, Quality const& quality) - : ammLiquidity_(ammLiquidity) - , amounts_(amounts) - , balances_(balances) - , quality_(quality) - , consumed_(false) + : ammLiquidity_(ammLiquidity), amounts_(amounts), balances_(balances), quality_(quality) + { } diff --git a/src/xrpld/app/paths/detail/BookStep.cpp b/src/libxrpl/tx/paths/BookStep.cpp similarity index 94% rename from src/xrpld/app/paths/detail/BookStep.cpp rename to src/libxrpl/tx/paths/BookStep.cpp index dca38f8df5..b17ccf1ea5 100644 --- a/src/xrpld/app/paths/detail/BookStep.cpp +++ b/src/libxrpl/tx/paths/BookStep.cpp @@ -1,19 +1,19 @@ -#include -#include - #include #include #include #include +#include +#include #include #include #include #include #include +#include +#include #include #include #include -#include #include @@ -63,7 +63,7 @@ protected: std::optional cache_; -public: +private: BookStep(StrandContext const& ctx, Issue const& in, Issue const& out) : book_(in, out, ctx.domainID) , strandSrc_(ctx.strandSrc) @@ -74,6 +74,7 @@ public: { if (auto const ammSle = ctx.view.read(keylet::amm(in, out)); ammSle && ammSle->getFieldAmount(sfLPTokenBalance) != beast::zero) + { ammLiquidity_.emplace( ctx.view, (*ammSle)[sfAccount], @@ -82,8 +83,10 @@ public: out, ctx.ammContext, ctx.j); + } } +public: Book const& book() const { @@ -223,6 +226,8 @@ private: // whichever is a better quality. std::optional tipOfferQualityF(ReadView const& view) const; + + friend TDerived; }; //------------------------------------------------------------------------------ @@ -240,7 +245,11 @@ class BookPaymentStep : public BookStep> public: explicit BookPaymentStep() = default; - using BookStep>::BookStep; + BookPaymentStep(StrandContext const& ctx, Issue const& in, Issue const& out) + : BookStep>(ctx, in, out) + { + } + using BookStep>::qualityUpperBound; using typename BookStep>::OfferType; @@ -442,7 +451,7 @@ public: std::uint32_t getOfrInRate(Step const* prevStep, AccountID const& owner, std::uint32_t trIn) const { - auto const srcAcct = prevStep ? prevStep->directStepSrcAcct() : std::nullopt; + auto const srcAcct = (prevStep != nullptr) ? prevStep->directStepSrcAcct() : std::nullopt; return owner == srcAcct // If offer crossing && prevStep is DirectI ? QUALITY_ONE // && src is offer owner @@ -457,9 +466,9 @@ public: AccountID const& strandDst, std::uint32_t trOut) const { - return // If offer crossing - prevStep && prevStep->bookStepBook() && // && prevStep is BookStep - owner == strandDst // && dest is offer owner + return // If offer crossing + (prevStep != nullptr) && prevStep->bookStepBook() && // && prevStep is BookStep + owner == strandDst // && dest is offer owner ? QUALITY_ONE : trOut; // then rate = QUALITY_ONE } @@ -484,11 +493,14 @@ public: // when calculating the upper bound quality and the quality function // because single path AMM's offer quality is not constant. if (!rules.enabled(fixAMMv1_1)) + { return ofrQ; - else if ( - offerType == OfferType::CLOB || + } + if (offerType == OfferType::CLOB || (this->ammLiquidity_ && this->ammLiquidity_->multiPath())) + { return ofrQ; + } auto rate = [&](AccountID const& id) { if (isXRP(id) || id == this->strandDst_) @@ -667,9 +679,13 @@ BookStep::forEachOffer( // Note that offer.quality() returns a (non-optional) Quality. So // ofrQ is always safe to use below this point in the lambda. if (!ofrQ) + { ofrQ = offer.quality(); + } else if (*ofrQ != offer.quality()) + { return false; + } if (static_cast(this)->limitSelfCrossQuality( strandSrc_, strandDst_, offer, ofrQ, offers, offerAttempted)) @@ -697,8 +713,10 @@ BookStep::forEachOffer( if (auto const key = offer.key()) offers.permRmOffer(*key); if (!offerAttempted) + { // Change quality only if no previous offers were tried. ofrQ = std::nullopt; + } // Returning true causes offers.step() to delete the offer. return true; } @@ -804,7 +822,7 @@ BookStep::consumeOffer( { auto const dr = offer.send(sb, book_.in.account, offer.owner(), toSTAmount(ofrAmt.in, book_.in), j_); - if (dr != tesSUCCESS) + if (!isTesSuccess(dr)) Throw(dr); } @@ -813,7 +831,7 @@ BookStep::consumeOffer( { auto const cr = offer.send(sb, offer.owner(), book_.out.account, toSTAmount(ownerGives, book_.out), j_); - if (cr != tesSUCCESS) + if (!isTesSuccess(cr)) Throw(cr); } @@ -874,24 +892,34 @@ auto BookStep::tipOfferQuality(ReadView const& view) const -> std::optional> { - if (auto const res = tip(view); !res) + auto const res = tip(view); + if (!res) + { return std::nullopt; - else if (auto const q = std::get_if(&(*res))) + } + if (auto const q = std::get_if(&(*res))) + { return std::make_pair(*q, OfferType::CLOB); - else - return std::make_pair(std::get>(*res).quality(), OfferType::AMM); + } + + return std::make_pair(std::get>(*res).quality(), OfferType::AMM); } template std::optional BookStep::tipOfferQualityF(ReadView const& view) const { - if (auto const res = tip(view); !res) + auto const res = tip(view); + if (!res) + { return std::nullopt; - else if (auto const q = std::get_if(&(*res))) + } + if (auto const q = std::get_if(&(*res))) + { return QualityFunction{*q, QualityFunction::CLOBLikeTag{}}; - else - return std::get>(*res).getQualityFunc(); + } + + return std::get>(*res).getQualityFunc(); } template @@ -947,33 +975,31 @@ BookStep::revImp( // we need to consume the offer return true; } - else - { - auto ofrAdjAmt = ofrAmt; - auto stpAdjAmt = stpAmt; - auto ownerGivesAdj = ownerGives; - limitStepOut( - offer, - ofrAdjAmt, - stpAdjAmt, - ownerGivesAdj, - transferRateIn, - transferRateOut, - remainingOut); - remainingOut = beast::zero; - savedIns.insert(stpAdjAmt.in); - savedOuts.insert(remainingOut); - result.in = sum(savedIns); - result.out = out; - this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj); - // Explicitly check whether the offer is funded. Given that we have - // (stpAmt.out > remainingOut), it's natural to assume the offer - // will still be funded after consuming remainingOut but that is - // not always the case. If the mantissas of two IOU amounts differ - // by less than ten, then subtracting them leaves a zero. - return offer.fully_consumed(); - } + auto ofrAdjAmt = ofrAmt; + auto stpAdjAmt = stpAmt; + auto ownerGivesAdj = ownerGives; + limitStepOut( + offer, + ofrAdjAmt, + stpAdjAmt, + ownerGivesAdj, + transferRateIn, + transferRateOut, + remainingOut); + remainingOut = beast::zero; + savedIns.insert(stpAdjAmt.in); + savedOuts.insert(remainingOut); + result.in = sum(savedIns); + result.out = out; + this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj); + + // Explicitly check whether the offer is funded. Given that we have + // (stpAmt.out > remainingOut), it's natural to assume the offer + // will still be funded after consuming remainingOut but that is + // not always the case. If the mantissas of two IOU amounts differ + // by less than ten, then subtracting them leaves a zero. + return offer.fully_consumed(); }; { @@ -983,7 +1009,7 @@ BookStep::revImp( return DebtDirection::issues; }(); auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer); - boost::container::flat_set toRm = std::move(std::get<0>(r)); + boost::container::flat_set const toRm = std::move(std::get<0>(r)); std::uint32_t const offersConsumed = std::get<1>(r); offersUsed_ = offersConsumed; SetUnion(ofrsToRm, toRm); @@ -1145,7 +1171,7 @@ BookStep::fwdImp( return DebtDirection::issues; }(); auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer); - boost::container::flat_set toRm = std::move(std::get<0>(r)); + boost::container::flat_set const toRm = std::move(std::get<0>(r)); std::uint32_t const offersConsumed = std::get<1>(r); offersUsed_ = offersConsumed; SetUnion(ofrsToRm, toRm); @@ -1233,13 +1259,14 @@ BookStep::check(StrandContext const& ctx) const // Do not allow two books to output the same issue. This may cause offers on // one step to unfund offers in another step. - if (!ctx.seenBookOuts.insert(book_.out).second || ctx.seenDirectIssues[0].count(book_.out)) + if (!ctx.seenBookOuts.insert(book_.out).second || + (ctx.seenDirectIssues[0].count(book_.out) != 0u)) { JLOG(j_.debug()) << "BookStep: loop detected: " << *this; return temBAD_PATH_LOOP; } - if (ctx.seenDirectIssues[1].count(book_.out)) + if (ctx.seenDirectIssues[1].count(book_.out) != 0u) { JLOG(j_.debug()) << "BookStep: loop detected: " << *this; return temBAD_PATH_LOOP; @@ -1255,7 +1282,7 @@ BookStep::check(StrandContext const& ctx) const return tecNO_ISSUER; } - if (ctx.prevStep) + if (ctx.prevStep != nullptr) { if (auto const prev = ctx.prevStep->directStepSrcAcct()) { @@ -1265,7 +1292,7 @@ BookStep::check(StrandContext const& ctx) const auto sle = view.read(keylet::line(*prev, cur, book_.in.currency)); if (!sle) return terNO_LINE; - if ((*sle)[sfFlags] & ((cur > *prev) ? lsfHighNoRipple : lsfLowNoRipple)) + if (((*sle)[sfFlags] & ((cur > *prev) ? lsfHighNoRipple : lsfLowNoRipple)) != 0u) return terNO_RIPPLE; } } @@ -1329,7 +1356,7 @@ make_BookStepHelper(StrandContext const& ctx, Issue const& in, Issue const& out) ter = paymentStep->check(ctx); r = std::move(paymentStep); } - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return {ter, nullptr}; return {tesSUCCESS, std::move(r)}; diff --git a/src/libxrpl/tx/paths/BookTip.cpp b/src/libxrpl/tx/paths/BookTip.cpp index f00da6d7c7..f9b700b7af 100644 --- a/src/libxrpl/tx/paths/BookTip.cpp +++ b/src/libxrpl/tx/paths/BookTip.cpp @@ -1,9 +1,11 @@ +#include +#include #include namespace xrpl { BookTip::BookTip(ApplyView& view, Book const& book) - : view_(view), m_valid(false), m_book(getBookBase(book)), m_end(getQualityNext(m_book)) + : view_(view), m_book(getBookBase(book)), m_end(getQualityNext(m_book)) { } diff --git a/src/xrpld/app/paths/detail/DirectStep.cpp b/src/libxrpl/tx/paths/DirectStep.cpp similarity index 92% rename from src/xrpld/app/paths/detail/DirectStep.cpp rename to src/libxrpl/tx/paths/DirectStep.cpp index 09ad2fcd67..9fd90b7755 100644 --- a/src/xrpld/app/paths/detail/DirectStep.cpp +++ b/src/libxrpl/tx/paths/DirectStep.cpp @@ -1,11 +1,11 @@ -#include - #include -#include #include +#include +#include #include #include #include +#include #include #include @@ -66,7 +66,7 @@ protected: std::pair qualities(ReadView const& sb, DebtDirection srcDebtDir, StrandDirection strandDir) const; -public: +private: DirectStepI( StrandContext const& ctx, AccountID const& src, @@ -81,6 +81,7 @@ public: { } +public: AccountID const& src() const { @@ -195,6 +196,8 @@ private: } return false; } + + friend TDerived; }; //------------------------------------------------------------------------------ @@ -209,18 +212,26 @@ private: class DirectIPaymentStep : public DirectStepI { public: - using DirectStepI::DirectStepI; + DirectIPaymentStep( + StrandContext const& ctx, + AccountID const& src, + AccountID const& dst, + Currency const& c) + : DirectStepI(ctx, src, dst, c) + { + } + using DirectStepI::check; - bool - verifyPrevStepDebtDirection(DebtDirection) const + static bool + verifyPrevStepDebtDirection(DebtDirection) { // A payment doesn't care whether or not prevStepRedeems. return true; } - bool - verifyDstQualityIn(std::uint32_t dstQIn) const + static bool + verifyDstQualityIn(std::uint32_t dstQIn) { // Payments have no particular expectations for what dstQIn will be. return true; @@ -252,11 +263,19 @@ public: class DirectIOfferCrossingStep : public DirectStepI { public: - using DirectStepI::DirectStepI; + DirectIOfferCrossingStep( + StrandContext const& ctx, + AccountID const& src, + AccountID const& dst, + Currency const& c) + : DirectStepI(ctx, src, dst, c) + { + } + using DirectStepI::check; - bool - verifyPrevStepDebtDirection(DebtDirection prevStepDir) const + static bool + verifyPrevStepDebtDirection(DebtDirection prevStepDir) { // During offer crossing we rely on the fact that prevStepRedeems // will *always* issue. That's because: @@ -268,16 +287,16 @@ public: return issues(prevStepDir); } - bool - verifyDstQualityIn(std::uint32_t dstQIn) const + static bool + verifyDstQualityIn(std::uint32_t dstQIn) { // Due to a couple of factors dstQIn is always QUALITY_ONE for // offer crossing. If that changes we need to know. return dstQIn == QUALITY_ONE; } - std::uint32_t - quality(ReadView const& sb, QualityDirection qDir) const; + static std::uint32_t + quality(ReadView const& sb, QualityDirection qDir); // Compute the maximum value that can flow from src->dst at // the best available quality. @@ -288,8 +307,8 @@ public: // Verify the consistency of the step. These checks are specific to // offer crossing and assume that general checks were already performed. - TER - check(StrandContext const& ctx, std::shared_ptr const& sleSrc) const; + static TER + check(StrandContext const& ctx, std::shared_ptr const& sleSrc); std::string logString() const override @@ -316,31 +335,33 @@ DirectIPaymentStep::quality(ReadView const& sb, QualityDirection qDir) const { // compute dst quality in if (this->dst_ < this->src_) + { return sfLowQualityIn; - else - return sfHighQualityIn; + } + + return sfHighQualityIn; } - else + + // compute src quality out + if (this->src_ < this->dst_) { - // compute src quality out - if (this->src_ < this->dst_) - return sfLowQualityOut; - else - return sfHighQualityOut; + return sfLowQualityOut; } + + return sfHighQualityOut; }(); if (!sle->isFieldPresent(field)) return QUALITY_ONE; auto const q = (*sle)[field]; - if (!q) + if (q == 0u) return QUALITY_ONE; return q; } std::uint32_t -DirectIOfferCrossingStep::quality(ReadView const&, QualityDirection qDir) const +DirectIOfferCrossingStep::quality(ReadView const&, QualityDirection qDir) { // If offer crossing then ignore trust line Quality fields. This // preserves a long-standing tradition. @@ -389,21 +410,21 @@ DirectIPaymentStep::check(StrandContext const& ctx, std::shared_ptr c auto const authField = (src_ > dst_) ? lsfHighAuth : lsfLowAuth; - if (((*sleSrc)[sfFlags] & lsfRequireAuth) && !((*sleLine)[sfFlags] & authField) && - (*sleLine)[sfBalance] == beast::zero) + if ((((*sleSrc)[sfFlags] & lsfRequireAuth) != 0u) && + (((*sleLine)[sfFlags] & authField) == 0u) && (*sleLine)[sfBalance] == beast::zero) { JLOG(j_.debug()) << "DirectStepI: can't receive IOUs from issuer without auth." << " src: " << src_; return terNO_AUTH; } - if (ctx.prevStep) + if (ctx.prevStep != nullptr) { if (ctx.prevStep->bookStepBook()) { auto const noRippleSrcToDst = ((*sleLine)[sfFlags] & ((src_ > dst_) ? lsfHighNoRipple : lsfLowNoRipple)); - if (noRippleSrcToDst) + if (noRippleSrcToDst != 0u) return terNO_RIPPLE; } } @@ -425,7 +446,7 @@ DirectIPaymentStep::check(StrandContext const& ctx, std::shared_ptr c } TER -DirectIOfferCrossingStep::check(StrandContext const&, std::shared_ptr const&) const +DirectIOfferCrossingStep::check(StrandContext const&, std::shared_ptr const&) { // The standard checks are all we can do because any remaining checks // require the existence of a trust line. Offer crossing does not @@ -498,7 +519,7 @@ DirectStepI::revImp( { IOUAmount const in = mulRatio(srcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true); cache_.emplace(in, srcToDst, out, srcDebtDir); - rippleCredit( + directSendNoFee( sb, src_, dst_, @@ -515,7 +536,7 @@ DirectStepI::revImp( IOUAmount const in = mulRatio(maxSrcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true); IOUAmount const actualOut = mulRatio(maxSrcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false); cache_.emplace(in, maxSrcToDst, actualOut, srcDebtDir); - rippleCredit( + directSendNoFee( sb, src_, dst_, @@ -607,7 +628,7 @@ DirectStepI::fwdImp( { IOUAmount const out = mulRatio(srcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false); setCacheLimiting(in, srcToDst, out, srcDebtDir); - rippleCredit( + directSendNoFee( sb, src_, dst_, @@ -624,7 +645,7 @@ DirectStepI::fwdImp( IOUAmount const actualIn = mulRatio(maxSrcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true); IOUAmount const out = mulRatio(maxSrcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false); setCacheLimiting(actualIn, maxSrcToDst, out, srcDebtDir); - rippleCredit( + directSendNoFee( sb, src_, dst_, @@ -692,7 +713,7 @@ template std::pair DirectStepI::qualitiesSrcRedeems(ReadView const& sb) const { - if (!prevStep_) + if (prevStep_ == nullptr) return {QUALITY_ONE, QUALITY_ONE}; auto const prevStepQIn = prevStep_->lineQualityIn(sb); @@ -737,15 +758,13 @@ DirectStepI::qualities( { return qualitiesSrcRedeems(sb); } - else - { - auto const prevStepDebtDirection = [&] { - if (prevStep_) - return prevStep_->debtDirection(sb, strandDir); - return DebtDirection::issues; - }(); - return qualitiesSrcIssues(sb, prevStepDebtDirection); - } + + auto const prevStepDebtDirection = [&] { + if (prevStep_) + return prevStep_->debtDirection(sb, strandDir); + return DebtDirection::issues; + }(); + return qualitiesSrcIssues(sb, prevStepDebtDirection); } template @@ -803,18 +822,18 @@ DirectStepI::check(StrandContext const& ctx) const if (!(ctx.isLast && ctx.isFirst)) { auto const ter = checkFreeze(ctx.view, src_, dst_, currency_); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return ter; } // If previous step was a direct step then we need to check // no ripple flags. - if (ctx.prevStep) + if (ctx.prevStep != nullptr) { if (auto prevSrc = ctx.prevStep->directStepSrcAcct()) { auto const ter = checkNoRipple(ctx.view, *prevSrc, src_, dst_, currency_, j_); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return ter; } } @@ -822,9 +841,9 @@ DirectStepI::check(StrandContext const& ctx) const Issue const srcIssue{currency_, src_}; Issue const dstIssue{currency_, dst_}; - if (ctx.seenBookOuts.count(srcIssue)) + if (ctx.seenBookOuts.count(srcIssue) != 0u) { - if (!ctx.prevStep) + if (ctx.prevStep == nullptr) { // LCOV_EXCL_START UNREACHABLE( @@ -885,7 +904,7 @@ make_DirectStepI( { TER ter = tefINTERNAL; std::unique_ptr r; - if (ctx.offerCrossing) + if (ctx.offerCrossing != 0u) { auto offerCrossingStep = std::make_unique(ctx, src, dst, c); ter = offerCrossingStep->check(ctx); @@ -897,7 +916,7 @@ make_DirectStepI( ter = paymentStep->check(ctx); r = std::move(paymentStep); } - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return {ter, nullptr}; return {tesSUCCESS, std::move(r)}; diff --git a/src/libxrpl/tx/paths/Flow.cpp b/src/libxrpl/tx/paths/Flow.cpp index 3e9adca814..5a706ea812 100644 --- a/src/libxrpl/tx/paths/Flow.cpp +++ b/src/libxrpl/tx/paths/Flow.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -15,10 +15,14 @@ static auto finishFlow(PaymentSandbox& sb, Issue const& srcIssue, Issue const& dstIssue, FlowResult&& f) { path::RippleCalc::Output result; - if (f.ter == tesSUCCESS) + if (isTesSuccess(f.ter)) + { f.sandbox->apply(sb); + } else + { result.removableOffers = std::move(f.removableOffers); + } result.setResult(f.ter); result.actualAmountIn = toSTAmount(f.in, srcIssue); @@ -78,7 +82,7 @@ flow( domainID, j); - if (toStrandsTer != tesSUCCESS) + if (!isTesSuccess(toStrandsTer)) { path::RippleCalc::Output result; result.setResult(toStrandsTer); diff --git a/src/libxrpl/tx/paths/OfferStream.cpp b/src/libxrpl/tx/paths/OfferStream.cpp index 596ae36b96..411b5cb05b 100644 --- a/src/libxrpl/tx/paths/OfferStream.cpp +++ b/src/libxrpl/tx/paths/OfferStream.cpp @@ -1,9 +1,11 @@ #include #include +#include +#include +#include #include #include #include -#include namespace xrpl { @@ -95,8 +97,10 @@ accountFundsHelper( beast::Journal j) { if (issue.account == id) + { // self funded return amtDefault; + } return toAmount( accountHolds(view, id, issue.currency, issue.account, freezeHandling, j)); @@ -199,7 +203,7 @@ TOfferStreamBase::step() if (!tip_.step(j_)) return false; - std::shared_ptr entry = tip_.entry(); + std::shared_ptr const entry = tip_.entry(); // If we exceed the maximum number of allowed steps, we're done. if (!counter_.step()) diff --git a/src/xrpld/app/paths/detail/PaySteps.cpp b/src/libxrpl/tx/paths/PaySteps.cpp similarity index 92% rename from src/xrpld/app/paths/detail/PaySteps.cpp rename to src/libxrpl/tx/paths/PaySteps.cpp index 7bbcb2a72c..33b63f7714 100644 --- a/src/xrpld/app/paths/detail/PaySteps.cpp +++ b/src/libxrpl/tx/paths/PaySteps.cpp @@ -55,8 +55,8 @@ toStep( { auto& j = ctx.j; - if (ctx.isFirst && e1->isAccount() && (e1->getNodeType() & STPathElement::typeCurrency) && - isXRP(e1->getCurrency())) + if (ctx.isFirst && e1->isAccount() && + ((e1->getNodeType() & STPathElement::typeCurrency) != 0u) && isXRP(e1->getCurrency())) { return make_XRPEndpointStep(ctx, e1->getAccountID()); } @@ -83,10 +83,12 @@ toStep( (e2->getNodeType() & STPathElement::typeCurrency) || (e2->getNodeType() & STPathElement::typeIssuer), "xrpl::toStep : currency or issuer"); - auto const outCurrency = - e2->getNodeType() & STPathElement::typeCurrency ? e2->getCurrency() : curIssue.currency; - auto const outIssuer = - e2->getNodeType() & STPathElement::typeIssuer ? e2->getIssuerID() : curIssue.account; + auto const outCurrency = ((e2->getNodeType() & STPathElement::typeCurrency) != 0u) + ? e2->getCurrency() + : curIssue.currency; + auto const outIssuer = ((e2->getNodeType() & STPathElement::typeIssuer) != 0u) + ? e2->getIssuerID() + : curIssue.account; if (isXRP(curIssue.currency) && isXRP(outCurrency)) { @@ -132,12 +134,12 @@ toStrand( { auto const t = pe.getNodeType(); - if ((t & ~STPathElement::typeAll) || !t) + if (((t & ~STPathElement::typeAll) != 0u) || (t == 0u)) return {temBAD_PATH, Strand{}}; - bool const hasAccount = t & STPathElement::typeAccount; - bool const hasIssuer = t & STPathElement::typeIssuer; - bool const hasCurrency = t & STPathElement::typeCurrency; + bool const hasAccount = (t & STPathElement::typeAccount) != 0u; + bool const hasIssuer = (t & STPathElement::typeIssuer) != 0u; + bool const hasCurrency = (t & STPathElement::typeCurrency) != 0u; if (hasAccount && (hasIssuer || hasCurrency)) return {temBAD_PATH, Strand{}}; @@ -192,7 +194,7 @@ toStrand( STPathElement const& lastCurrency = *std::find_if(normPath.rbegin(), normPath.rend(), hasCurrency); if ((lastCurrency.getCurrency() != deliver.currency) || - (offerCrossing && lastCurrency.getIssuerID() != deliver.account)) + ((offerCrossing != 0u) && lastCurrency.getIssuerID() != deliver.account)) { normPath.emplace_back(std::nullopt, deliver.currency, deliver.account); } @@ -264,9 +266,13 @@ toStrand( auto const next = &normPath[i + 1]; if (cur->isAccount()) + { curIssue.account = cur->getAccountID(); + } else if (cur->hasIssuer()) + { curIssue.account = cur->getIssuerID(); + } if (cur->hasCurrency()) { @@ -283,7 +289,7 @@ toStrand( JLOG(j.trace()) << "Inserting implied account"; auto msr = make_DirectStepI( ctx(), cur->getAccountID(), curIssue.account, curIssue.currency); - if (msr.first != tesSUCCESS) + if (!isTesSuccess(msr.first)) return {msr.first, Strand{}}; result.push_back(std::move(msr.second)); impliedPE.emplace( @@ -298,7 +304,7 @@ toStrand( JLOG(j.trace()) << "Inserting implied account before offer"; auto msr = make_DirectStepI( ctx(), cur->getAccountID(), curIssue.account, curIssue.currency); - if (msr.first != tesSUCCESS) + if (!isTesSuccess(msr.first)) return {msr.first, Strand{}}; result.push_back(std::move(msr.second)); impliedPE.emplace( @@ -314,21 +320,19 @@ toStrand( { if (i != normPath.size() - 2) return {temBAD_PATH, Strand{}}; - else - { - // Last step. insert xrp endpoint step - auto msr = make_XRPEndpointStep(ctx(), next->getAccountID()); - if (msr.first != tesSUCCESS) - return {msr.first, Strand{}}; - result.push_back(std::move(msr.second)); - } + + // Last step. insert xrp endpoint step + auto msr = make_XRPEndpointStep(ctx(), next->getAccountID()); + if (!isTesSuccess(msr.first)) + return {msr.first, Strand{}}; + result.push_back(std::move(msr.second)); } else { JLOG(j.trace()) << "Inserting implied account after offer"; auto msr = make_DirectStepI( ctx(), curIssue.account, next->getAccountID(), curIssue.currency); - if (msr.first != tesSUCCESS) + if (!isTesSuccess(msr.first)) return {msr.first, Strand{}}; result.push_back(std::move(msr.second)); } @@ -346,8 +350,10 @@ toStrand( } auto s = toStep(ctx(/*isLast*/ i == normPath.size() - 2), cur, next, curIssue); - if (s.first == tesSUCCESS) + if (isTesSuccess(s.first)) + { result.emplace_back(std::move(s.second)); + } else { JLOG(j.debug()) << "toStep failed: " << s.first; @@ -457,7 +463,7 @@ toStrands( auto const ter = sp.first; auto& strand = sp.second; - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) { JLOG(j.trace()) << "failed to add default path"; if (isTemMalformed(ter) || paths.empty()) @@ -501,7 +507,7 @@ toStrands( auto ter = sp.first; auto& strand = sp.second; - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) { lastFailTer = ter; JLOG(j.trace()) << "failed to add path: ter: " << ter diff --git a/src/libxrpl/tx/paths/RippleCalc.cpp b/src/libxrpl/tx/paths/RippleCalc.cpp index e87ecab90f..c9f0c9e0fb 100644 --- a/src/libxrpl/tx/paths/RippleCalc.cpp +++ b/src/libxrpl/tx/paths/RippleCalc.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -35,17 +34,17 @@ RippleCalc::rippleCalculate( STPathSet const& spsPaths, std::optional const& domainID, - Logs& l, + ServiceRegistry& registry, Input const* const pInputs) { Output flowOut; PaymentSandbox flowSB(&view); - auto j = l.journal("Flow"); + auto j = registry.getJournal("Flow"); { - bool const defaultPaths = !pInputs ? true : pInputs->defaultPathsAllowed; + bool const defaultPaths = (pInputs == nullptr) ? true : pInputs->defaultPathsAllowed; - bool const partialPayment = !pInputs ? false : pInputs->partialPaymentAllowed; + bool const partialPayment = (pInputs == nullptr) ? false : pInputs->partialPaymentAllowed; auto const limitQuality = [&]() -> std::optional { if (pInputs && pInputs->limitQuality && saMaxAmountReq > beast::zero) diff --git a/src/xrpld/app/paths/detail/XRPEndpointStep.cpp b/src/libxrpl/tx/paths/XRPEndpointStep.cpp similarity index 95% rename from src/xrpld/app/paths/detail/XRPEndpointStep.cpp rename to src/libxrpl/tx/paths/XRPEndpointStep.cpp index ceb166d6ec..7452f57ecd 100644 --- a/src/xrpld/app/paths/detail/XRPEndpointStep.cpp +++ b/src/libxrpl/tx/paths/XRPEndpointStep.cpp @@ -1,13 +1,13 @@ -#include - #include -#include #include +#include +#include #include #include #include #include #include +#include #include #include @@ -37,12 +37,12 @@ private: return EitherAmount(*cache_); } -public: XRPEndpointStep(StrandContext const& ctx, AccountID const& acc) : acc_(acc), isLast_(ctx.isLast), j_(ctx.j) { } +public: AccountID const& acc() const { @@ -135,6 +135,8 @@ private: } return false; } + + friend TDerived; }; //------------------------------------------------------------------------------ @@ -149,7 +151,10 @@ private: class XRPEndpointPaymentStep : public XRPEndpointStep { public: - using XRPEndpointStep::XRPEndpointStep; + XRPEndpointPaymentStep(StrandContext const& ctx, AccountID const& acc) + : XRPEndpointStep(ctx, acc) + { + } XRPAmount xrpLiquid(ReadView& sb) const @@ -238,7 +243,7 @@ XRPEndpointStep::revImp( auto& sender = isLast_ ? xrpAccount() : acc_; auto& receiver = isLast_ ? acc_ : xrpAccount(); auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return {XRPAmount{beast::zero}, XRPAmount{beast::zero}}; cache_.emplace(result); @@ -261,7 +266,7 @@ XRPEndpointStep::fwdImp( auto& sender = isLast_ ? xrpAccount() : acc_; auto& receiver = isLast_ ? acc_ : xrpAccount(); auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return {XRPAmount{beast::zero}, XRPAmount{beast::zero}}; cache_.emplace(result); @@ -327,7 +332,7 @@ XRPEndpointStep::check(StrandContext const& ctx) const auto& src = isLast_ ? xrpAccount() : acc_; auto& dst = isLast_ ? acc_ : xrpAccount(); auto ter = checkFreeze(ctx.view, src, dst, xrpCurrency()); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return ter; auto const issuesIndex = isLast_ ? 0 : 1; @@ -363,7 +368,7 @@ make_XRPEndpointStep(StrandContext const& ctx, AccountID const& acc) { TER ter = tefINTERNAL; std::unique_ptr r; - if (ctx.offerCrossing) + if (ctx.offerCrossing != 0u) { auto offerCrossingStep = std::make_unique(ctx, acc); ter = offerCrossingStep->check(ctx); @@ -375,7 +380,7 @@ make_XRPEndpointStep(StrandContext const& ctx, AccountID const& acc) ter = paymentStep->check(ctx); r = std::move(paymentStep); } - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return {ter, nullptr}; return {tesSUCCESS, std::move(r)}; diff --git a/src/libxrpl/tx/transactors/account/DeleteAccount.cpp b/src/libxrpl/tx/transactors/account/AccountDelete.cpp similarity index 84% rename from src/libxrpl/tx/transactors/account/DeleteAccount.cpp rename to src/libxrpl/tx/transactors/account/AccountDelete.cpp index a9f7dbe674..c7f71a6cf4 100644 --- a/src/libxrpl/tx/transactors/account/DeleteAccount.cpp +++ b/src/libxrpl/tx/transactors/account/AccountDelete.cpp @@ -1,38 +1,38 @@ #include -#include #include -#include #include +#include +#include +#include +#include #include #include #include #include #include -#include -#include +#include +#include #include -#include -#include -#include +#include +#include #include namespace xrpl { bool -DeleteAccount::checkExtraFeatures(PreflightContext const& ctx) +AccountDelete::checkExtraFeatures(PreflightContext const& ctx) { - if (ctx.tx.isFieldPresent(sfCredentialIDs) && !ctx.rules.enabled(featureCredentials)) - return false; - - return true; + return !ctx.tx.isFieldPresent(sfCredentialIDs) || ctx.rules.enabled(featureCredentials); } NotTEC -DeleteAccount::preflight(PreflightContext const& ctx) +AccountDelete::preflight(PreflightContext const& ctx) { if (ctx.tx[sfAccount] == ctx.tx[sfDestination]) + { // An account cannot be deleted and give itself the resulting XRP. return temDST_IS_SRC; + } if (auto const err = credentials::checkFields(ctx.tx, ctx.j); !isTesSuccess(err)) return err; @@ -41,7 +41,7 @@ DeleteAccount::preflight(PreflightContext const& ctx) } XRPAmount -DeleteAccount::calculateBaseFee(ReadView const& view, STTx const& tx) +AccountDelete::calculateBaseFee(ReadView const& view, STTx const& tx) { // The fee required for AccountDelete is one owner reserve. return calculateOwnerReserveFee(view, tx); @@ -79,7 +79,7 @@ removeSignersFromLedger( std::shared_ptr const& sleDel, beast::Journal j) { - return SetSignerList::removeFromLedger(registry, view, account, j); + return SignerListSet::removeFromLedger(registry, view, account, j); } TER @@ -142,7 +142,7 @@ removeOracleFromLedger( std::shared_ptr const& sleDel, beast::Journal j) { - return DeleteOracle::deleteOracle(view, sleDel, account, j); + return OracleDelete::deleteOracle(view, sleDel, account, j); } TER @@ -203,7 +203,7 @@ nonObligationDeleter(LedgerEntryType t) } // namespace TER -DeleteAccount::preclaim(PreclaimContext const& ctx) +AccountDelete::preclaim(PreclaimContext const& ctx) { AccountID const account{ctx.tx[sfAccount]}; AccountID const dst{ctx.tx[sfDestination]}; @@ -213,7 +213,7 @@ DeleteAccount::preclaim(PreclaimContext const& ctx) if (!sleDst) return tecNO_DST; - if ((*sleDst)[sfFlags] & lsfRequireDestTag && !ctx.tx[~sfDestinationTag]) + if ((((*sleDst)[sfFlags] & lsfRequireDestTag) != 0u) && !ctx.tx[~sfDestinationTag]) return tecDST_TAG_NEEDED; // If credentials are provided - check them anyway @@ -225,7 +225,7 @@ DeleteAccount::preclaim(PreclaimContext const& ctx) if (!ctx.tx.isFieldPresent(sfCredentialIDs)) { // Check whether the destination account requires deposit authorization. - if (sleDst->getFlags() & lsfDepositAuth) + if ((sleDst->getFlags() & lsfDepositAuth) != 0u) { if (!ctx.view.exists(keylet::depositPreauth(dst, account))) return tecNO_PERMISSION; @@ -233,7 +233,7 @@ DeleteAccount::preclaim(PreclaimContext const& ctx) } auto sleAccount = ctx.view.read(keylet::account(account)); - XRPL_ASSERT(sleAccount, "xrpl::DeleteAccount::preclaim : non-null account"); + XRPL_ASSERT(sleAccount, "xrpl::AccountDelete::preclaim : non-null account"); if (!sleAccount) return terNO_ACCOUNT; @@ -292,7 +292,7 @@ DeleteAccount::preclaim(PreclaimContext const& ctx) if (!cdirFirst(ctx.view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry)) return tesSUCCESS; - std::int32_t deletableDirEntryCount{0}; + std::uint32_t deletableDirEntryCount{0}; do { // Make sure any directory node types that we find are the kind @@ -302,7 +302,7 @@ DeleteAccount::preclaim(PreclaimContext const& ctx) { // Directory node has an invalid index. Bail out. // LCOV_EXCL_START - JLOG(ctx.j.fatal()) << "DeleteAccount: directory node in ledger " << ctx.view.seq() + JLOG(ctx.j.fatal()) << "AccountDelete: directory node in ledger " << ctx.view.seq() << " has index to object that is missing: " << to_string(dirEntry); return tefBAD_LEDGER; // LCOV_EXCL_STOP @@ -310,7 +310,7 @@ DeleteAccount::preclaim(PreclaimContext const& ctx) LedgerEntryType const nodeType{safe_cast((*sleItem)[sfLedgerEntryType])}; - if (!nonObligationDeleter(nodeType)) + if (nonObligationDeleter(nodeType) == nullptr) return tecHAS_OBLIGATIONS; // We found a deletable directory entry. Count it. If we find too @@ -324,14 +324,14 @@ DeleteAccount::preclaim(PreclaimContext const& ctx) } TER -DeleteAccount::doApply() +AccountDelete::doApply() { auto src = view().peek(keylet::account(account_)); - XRPL_ASSERT(src, "xrpl::DeleteAccount::doApply : non-null source account"); + XRPL_ASSERT(src, "xrpl::AccountDelete::doApply : non-null source account"); auto const dstID = ctx_.tx[sfDestination]; auto dst = view().peek(keylet::account(dstID)); - XRPL_ASSERT(dst, "xrpl::DeleteAccount::doApply : non-null destination account"); + XRPL_ASSERT(dst, "xrpl::AccountDelete::doApply : non-null destination account"); if (!src || !dst) return tefBAD_LEDGER; // LCOV_EXCL_LINE @@ -360,35 +360,36 @@ DeleteAccount::doApply() // LCOV_EXCL_START UNREACHABLE( - "xrpl::DeleteAccount::doApply : undeletable item not found " + "xrpl::AccountDelete::doApply : undeletable item not found " "in preclaim"); - JLOG(j_.error()) << "DeleteAccount undeletable item not " + JLOG(j_.error()) << "AccountDelete undeletable item not " "found in preclaim."; return {tecHAS_OBLIGATIONS, SkipEntry::No}; // LCOV_EXCL_STOP }, ctx_.journal); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return ter; // Transfer any XRP remaining after the fee is paid to the destination: - (*dst)[sfBalance] = (*dst)[sfBalance] + mSourceBalance; - (*src)[sfBalance] = (*src)[sfBalance] - mSourceBalance; - ctx_.deliver(mSourceBalance); + auto const remainingBalance = src->getFieldAmount(sfBalance).xrp(); + (*dst)[sfBalance] = (*dst)[sfBalance] + remainingBalance; + (*src)[sfBalance] = (*src)[sfBalance] - remainingBalance; + ctx_.deliver(remainingBalance); XRPL_ASSERT( - (*src)[sfBalance] == XRPAmount(0), "xrpl::DeleteAccount::doApply : source balance is zero"); + (*src)[sfBalance] == XRPAmount(0), "xrpl::AccountDelete::doApply : source balance is zero"); // If there's still an owner directory associated with the source account // delete it. if (view().exists(ownerDirKeylet) && !view().emptyDirDelete(ownerDirKeylet)) { - JLOG(j_.error()) << "DeleteAccount cannot delete root dir node of " << toBase58(account_); + JLOG(j_.error()) << "AccountDelete cannot delete root dir node of " << toBase58(account_); return tecHAS_OBLIGATIONS; } // Re-arm the password change fee if we can and need to. - if (mSourceBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent)) + if (remainingBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent)) dst->clearFlag(lsfPasswordSpent); view().update(dst); diff --git a/src/libxrpl/tx/transactors/account/SetAccount.cpp b/src/libxrpl/tx/transactors/account/AccountSet.cpp similarity index 81% rename from src/libxrpl/tx/transactors/account/SetAccount.cpp rename to src/libxrpl/tx/transactors/account/AccountSet.cpp index 32eb2afbb8..093019766c 100644 --- a/src/libxrpl/tx/transactors/account/SetAccount.cpp +++ b/src/libxrpl/tx/transactors/account/AccountSet.cpp @@ -1,19 +1,20 @@ #include #include +#include +#include #include #include #include #include #include -#include -#include +#include namespace xrpl { TxConsequences -SetAccount::makeTxConsequences(PreflightContext const& ctx) +AccountSet::makeTxConsequences(PreflightContext const& ctx) { - // The SetAccount may be a blocker, but only if it sets or clears + // The AccountSet may be a blocker, but only if it sets or clears // specific account flags. auto getTxConsequencesCategory = [](STTx const& tx) { if (std::uint32_t const uTxFlags = tx.getFlags(); @@ -37,13 +38,13 @@ SetAccount::makeTxConsequences(PreflightContext const& ctx) } std::uint32_t -SetAccount::getFlagsMask(PreflightContext const& ctx) +AccountSet::getFlagsMask(PreflightContext const& ctx) { return tfAccountSetMask; } NotTEC -SetAccount::preflight(PreflightContext const& ctx) +AccountSet::preflight(PreflightContext const& ctx) { auto& tx = ctx.tx; auto& j = ctx.j; @@ -62,8 +63,9 @@ SetAccount::preflight(PreflightContext const& ctx) // // RequireAuth // - bool bSetRequireAuth = (uTxFlags & tfRequireAuth) || (uSetFlag == asfRequireAuth); - bool bClearRequireAuth = (uTxFlags & tfOptionalAuth) || (uClearFlag == asfRequireAuth); + bool const bSetRequireAuth = ((uTxFlags & tfRequireAuth) != 0u) || (uSetFlag == asfRequireAuth); + bool const bClearRequireAuth = + ((uTxFlags & tfOptionalAuth) != 0u) || (uClearFlag == asfRequireAuth); if (bSetRequireAuth && bClearRequireAuth) { @@ -74,8 +76,10 @@ SetAccount::preflight(PreflightContext const& ctx) // // RequireDestTag // - bool bSetRequireDest = (uTxFlags & tfRequireDestTag) || (uSetFlag == asfRequireDest); - bool bClearRequireDest = (uTxFlags & tfOptionalDestTag) || (uClearFlag == asfRequireDest); + bool const bSetRequireDest = + ((uTxFlags & tfRequireDestTag) != 0u) || (uSetFlag == asfRequireDest); + bool const bClearRequireDest = + ((uTxFlags & tfOptionalDestTag) != 0u) || (uClearFlag == asfRequireDest); if (bSetRequireDest && bClearRequireDest) { @@ -86,8 +90,9 @@ SetAccount::preflight(PreflightContext const& ctx) // // DisallowXRP // - bool bSetDisallowXRP = (uTxFlags & tfDisallowXRP) || (uSetFlag == asfDisallowXRP); - bool bClearDisallowXRP = (uTxFlags & tfAllowXRP) || (uClearFlag == asfDisallowXRP); + bool const bSetDisallowXRP = ((uTxFlags & tfDisallowXRP) != 0u) || (uSetFlag == asfDisallowXRP); + bool const bClearDisallowXRP = + ((uTxFlags & tfAllowXRP) != 0u) || (uClearFlag == asfDisallowXRP); if (bSetDisallowXRP && bClearDisallowXRP) { @@ -98,9 +103,9 @@ SetAccount::preflight(PreflightContext const& ctx) // TransferRate if (tx.isFieldPresent(sfTransferRate)) { - std::uint32_t uRate = tx.getFieldU32(sfTransferRate); + std::uint32_t const uRate = tx.getFieldU32(sfTransferRate); - if (uRate && (uRate < QUALITY_ONE)) + if ((uRate != 0u) && (uRate < QUALITY_ONE)) { JLOG(j.trace()) << "Malformed transaction: Transfer rate too small."; return temBAD_TRANSFER_RATE; @@ -117,7 +122,8 @@ SetAccount::preflight(PreflightContext const& ctx) if (tx.isFieldPresent(sfTickSize)) { auto uTickSize = tx[sfTickSize]; - if (uTickSize && ((uTickSize < Quality::minTickSize) || (uTickSize > Quality::maxTickSize))) + if ((uTickSize != 0u) && + ((uTickSize < Quality::minTickSize) || (uTickSize > Quality::maxTickSize))) { JLOG(j.trace()) << "Malformed transaction: Bad tick size."; return temBAD_TICK_SIZE; @@ -126,7 +132,7 @@ SetAccount::preflight(PreflightContext const& ctx) if (auto const mk = tx[~sfMessageKey]) { - if (mk->size() && !publicKeyType({mk->data(), mk->size()})) + if (!mk->empty() && !publicKeyType({mk->data(), mk->size()})) { JLOG(j.trace()) << "Invalid message key specified."; return telBAD_PUBLIC_KEY; @@ -150,9 +156,9 @@ SetAccount::preflight(PreflightContext const& ctx) } NotTEC -SetAccount::checkPermission(ReadView const& view, STTx const& tx) +AccountSet::checkPermission(ReadView const& view, STTx const& tx) { - // SetAccount is prohibited to be granted on a transaction level, + // AccountSet is prohibited to be granted on a transaction level, // but some granular permissions are allowed. auto const delegate = tx[~sfDelegate]; if (!delegate) @@ -174,7 +180,7 @@ SetAccount::checkPermission(ReadView const& view, STTx const& tx) // AccountSet transaction. If any delegated account is trying to // update the flag on behalf of another account, it is not // authorized. - if (uSetFlag != 0 || uClearFlag != 0 || uTxFlags & tfUniversalMask) + if (uSetFlag != 0 || uClearFlag != 0 || ((uTxFlags & tfUniversalMask) != 0u)) return terNO_DELEGATE_PERMISSION; if (tx.isFieldPresent(sfEmailHash) && !granularPermissions.contains(AccountEmailHashSet)) @@ -199,7 +205,7 @@ SetAccount::checkPermission(ReadView const& view, STTx const& tx) } TER -SetAccount::preclaim(PreclaimContext const& ctx) +AccountSet::preclaim(PreclaimContext const& ctx) { auto const id = ctx.tx[sfAccount]; @@ -214,17 +220,17 @@ SetAccount::preclaim(PreclaimContext const& ctx) std::uint32_t const uSetFlag = ctx.tx.getFieldU32(sfSetFlag); // legacy AccountSet flags - bool bSetRequireAuth = (uTxFlags & tfRequireAuth) || (uSetFlag == asfRequireAuth); + bool const bSetRequireAuth = ((uTxFlags & tfRequireAuth) != 0u) || (uSetFlag == asfRequireAuth); // // RequireAuth // - if (bSetRequireAuth && !(uFlagsIn & lsfRequireAuth)) + if (bSetRequireAuth && ((uFlagsIn & lsfRequireAuth) == 0u)) { if (!dirIsEmpty(ctx.view, keylet::ownerDir(id))) { JLOG(ctx.j.trace()) << "Retry: Owner directory not empty."; - return (ctx.flags & tapRETRY) ? TER{terOWNERS} : TER{tecOWNERS}; + return ((ctx.flags & tapRETRY) != 0u) ? TER{terOWNERS} : TER{tecOWNERS}; } } @@ -235,7 +241,7 @@ SetAccount::preclaim(PreclaimContext const& ctx) { if (uSetFlag == asfAllowTrustLineClawback) { - if (uFlagsIn & lsfNoFreeze) + if ((uFlagsIn & lsfNoFreeze) != 0u) { JLOG(ctx.j.trace()) << "Can't set Clawback if NoFreeze is set"; return tecNO_PERMISSION; @@ -250,7 +256,7 @@ SetAccount::preclaim(PreclaimContext const& ctx) else if (uSetFlag == asfNoFreeze) { // Cannot set NoFreeze if clawback is enabled - if (uFlagsIn & lsfAllowTrustLineClawback) + if ((uFlagsIn & lsfAllowTrustLineClawback) != 0u) { JLOG(ctx.j.trace()) << "Can't set NoFreeze if clawback is enabled"; return tecNO_PERMISSION; @@ -262,7 +268,7 @@ SetAccount::preclaim(PreclaimContext const& ctx) } TER -SetAccount::doApply() +AccountSet::doApply() { auto const sle = view().peek(keylet::account(account_)); if (!sle) @@ -277,12 +283,15 @@ SetAccount::doApply() // legacy AccountSet flags std::uint32_t const uTxFlags{tx.getFlags()}; - bool const bSetRequireDest{(uTxFlags & tfRequireDestTag) || (uSetFlag == asfRequireDest)}; - bool const bClearRequireDest{(uTxFlags & tfOptionalDestTag) || (uClearFlag == asfRequireDest)}; - bool const bSetRequireAuth{(uTxFlags & tfRequireAuth) || (uSetFlag == asfRequireAuth)}; - bool const bClearRequireAuth{(uTxFlags & tfOptionalAuth) || (uClearFlag == asfRequireAuth)}; - bool const bSetDisallowXRP{(uTxFlags & tfDisallowXRP) || (uSetFlag == asfDisallowXRP)}; - bool const bClearDisallowXRP{(uTxFlags & tfAllowXRP) || (uClearFlag == asfDisallowXRP)}; + bool const bSetRequireDest{ + ((uTxFlags & tfRequireDestTag) != 0u) || (uSetFlag == asfRequireDest)}; + bool const bClearRequireDest{ + ((uTxFlags & tfOptionalDestTag) != 0u) || (uClearFlag == asfRequireDest)}; + bool const bSetRequireAuth{((uTxFlags & tfRequireAuth) != 0u) || (uSetFlag == asfRequireAuth)}; + bool const bClearRequireAuth{ + ((uTxFlags & tfOptionalAuth) != 0u) || (uClearFlag == asfRequireAuth)}; + bool const bSetDisallowXRP{((uTxFlags & tfDisallowXRP) != 0u) || (uSetFlag == asfDisallowXRP)}; + bool const bClearDisallowXRP{((uTxFlags & tfAllowXRP) != 0u) || (uClearFlag == asfDisallowXRP)}; bool const sigWithMaster{[&tx, &acct = account_]() { auto const spk = tx.getSigningPubKey(); @@ -300,13 +309,13 @@ SetAccount::doApply() // // RequireAuth // - if (bSetRequireAuth && !(uFlagsIn & lsfRequireAuth)) + if (bSetRequireAuth && ((uFlagsIn & lsfRequireAuth) == 0u)) { JLOG(j_.trace()) << "Set RequireAuth."; uFlagsOut |= lsfRequireAuth; } - if (bClearRequireAuth && (uFlagsIn & lsfRequireAuth)) + if (bClearRequireAuth && ((uFlagsIn & lsfRequireAuth) != 0u)) { JLOG(j_.trace()) << "Clear RequireAuth."; uFlagsOut &= ~lsfRequireAuth; @@ -315,13 +324,13 @@ SetAccount::doApply() // // RequireDestTag // - if (bSetRequireDest && !(uFlagsIn & lsfRequireDestTag)) + if (bSetRequireDest && ((uFlagsIn & lsfRequireDestTag) == 0u)) { JLOG(j_.trace()) << "Set lsfRequireDestTag."; uFlagsOut |= lsfRequireDestTag; } - if (bClearRequireDest && (uFlagsIn & lsfRequireDestTag)) + if (bClearRequireDest && ((uFlagsIn & lsfRequireDestTag) != 0u)) { JLOG(j_.trace()) << "Clear lsfRequireDestTag."; uFlagsOut &= ~lsfRequireDestTag; @@ -330,13 +339,13 @@ SetAccount::doApply() // // DisallowXRP // - if (bSetDisallowXRP && !(uFlagsIn & lsfDisallowXRP)) + if (bSetDisallowXRP && ((uFlagsIn & lsfDisallowXRP) == 0u)) { JLOG(j_.trace()) << "Set lsfDisallowXRP."; uFlagsOut |= lsfDisallowXRP; } - if (bClearDisallowXRP && (uFlagsIn & lsfDisallowXRP)) + if (bClearDisallowXRP && ((uFlagsIn & lsfDisallowXRP) != 0u)) { JLOG(j_.trace()) << "Clear lsfDisallowXRP."; uFlagsOut &= ~lsfDisallowXRP; @@ -345,7 +354,7 @@ SetAccount::doApply() // // DisableMaster // - if ((uSetFlag == asfDisableMaster) && !(uFlagsIn & lsfDisableMaster)) + if ((uSetFlag == asfDisableMaster) && ((uFlagsIn & lsfDisableMaster) == 0u)) { if (!sigWithMaster) { @@ -363,7 +372,7 @@ SetAccount::doApply() uFlagsOut |= lsfDisableMaster; } - if ((uClearFlag == asfDisableMaster) && (uFlagsIn & lsfDisableMaster)) + if ((uClearFlag == asfDisableMaster) && ((uFlagsIn & lsfDisableMaster) != 0u)) { JLOG(j_.trace()) << "Clear lsfDisableMaster."; uFlagsOut &= ~lsfDisableMaster; @@ -388,7 +397,7 @@ SetAccount::doApply() // if (uSetFlag == asfNoFreeze) { - if (!sigWithMaster && !(uFlagsIn & lsfDisableMaster)) + if (!sigWithMaster && ((uFlagsIn & lsfDisableMaster) == 0u)) { JLOG(j_.trace()) << "Must use master key to set NoFreeze."; return tecNEED_MASTER_KEY; @@ -491,7 +500,7 @@ SetAccount::doApply() if (messageKey.empty()) { - JLOG(j_.debug()) << "set message key"; + JLOG(j_.debug()) << "clear message key"; sle->makeFieldAbsent(sfMessageKey); } else @@ -525,7 +534,7 @@ SetAccount::doApply() // if (tx.isFieldPresent(sfTransferRate)) { - std::uint32_t uRate = tx.getFieldU32(sfTransferRate); + std::uint32_t const uRate = tx.getFieldU32(sfTransferRate); if (uRate == 0 || uRate == QUALITY_ONE) { @@ -565,32 +574,52 @@ SetAccount::doApply() sle->makeFieldAbsent(sfNFTokenMinter); if (uSetFlag == asfDisallowIncomingNFTokenOffer) + { uFlagsOut |= lsfDisallowIncomingNFTokenOffer; + } else if (uClearFlag == asfDisallowIncomingNFTokenOffer) + { uFlagsOut &= ~lsfDisallowIncomingNFTokenOffer; + } if (uSetFlag == asfDisallowIncomingCheck) + { uFlagsOut |= lsfDisallowIncomingCheck; + } else if (uClearFlag == asfDisallowIncomingCheck) + { uFlagsOut &= ~lsfDisallowIncomingCheck; + } if (uSetFlag == asfDisallowIncomingPayChan) + { uFlagsOut |= lsfDisallowIncomingPayChan; + } else if (uClearFlag == asfDisallowIncomingPayChan) + { uFlagsOut &= ~lsfDisallowIncomingPayChan; + } if (uSetFlag == asfDisallowIncomingTrustline) + { uFlagsOut |= lsfDisallowIncomingTrustline; + } else if (uClearFlag == asfDisallowIncomingTrustline) + { uFlagsOut &= ~lsfDisallowIncomingTrustline; + } // Set or clear flags for disallowing escrow if (ctx_.view().rules().enabled(featureTokenEscrow)) { if (uSetFlag == asfAllowTrustLineLocking) + { uFlagsOut |= lsfAllowTrustLineLocking; + } else if (uClearFlag == asfAllowTrustLineLocking) + { uFlagsOut &= ~lsfAllowTrustLineLocking; + } } // Set flag for clawback diff --git a/src/libxrpl/tx/transactors/account/SetRegularKey.cpp b/src/libxrpl/tx/transactors/account/SetRegularKey.cpp index 08175b196f..471da26c88 100644 --- a/src/libxrpl/tx/transactors/account/SetRegularKey.cpp +++ b/src/libxrpl/tx/transactors/account/SetRegularKey.cpp @@ -17,7 +17,7 @@ SetRegularKey::calculateBaseFee(ReadView const& view, STTx const& tx) { auto const sle = view.read(keylet::account(id)); - if (sle && (!(sle->getFlags() & lsfPasswordSpent))) + if (sle && ((sle->getFlags() & lsfPasswordSpent) == 0u)) { // flag is armed and they signed with the right account return XRPAmount{0}; diff --git a/src/libxrpl/tx/transactors/account/SetSignerList.cpp b/src/libxrpl/tx/transactors/account/SignerListSet.cpp similarity index 88% rename from src/libxrpl/tx/transactors/account/SetSignerList.cpp rename to src/libxrpl/tx/transactors/account/SignerListSet.cpp index d5b41c6acf..90ab8daf6f 100644 --- a/src/libxrpl/tx/transactors/account/SetSignerList.cpp +++ b/src/libxrpl/tx/transactors/account/SignerListSet.cpp @@ -1,13 +1,13 @@ -#include #include -#include +#include +#include #include #include #include #include #include #include -#include +#include #include #include @@ -19,8 +19,8 @@ namespace xrpl { // setting the sfSignerListID to zero in all cases. static std::uint32_t const DEFAULT_SIGNER_LIST_ID = 0; -std::tuple, SetSignerList::Operation> -SetSignerList::determineOperation(STTx const& tx, ApplyFlags flags, beast::Journal j) +std::tuple, SignerListSet::Operation> +SignerListSet::determineOperation(STTx const& tx, ApplyFlags flags, beast::Journal j) { // Check the quorum. A non-zero quorum means we're creating or replacing // the list. A zero quorum means we're destroying the list. @@ -29,7 +29,7 @@ SetSignerList::determineOperation(STTx const& tx, ApplyFlags flags, beast::Journ Operation op = unknown; bool const hasSignerEntries(tx.isFieldPresent(sfSignerEntries)); - if (quorum && hasSignerEntries) + if ((quorum != 0u) && hasSignerEntries) { auto signers = SignerEntries::deserialize(tx, j, "transaction"); @@ -51,18 +51,18 @@ SetSignerList::determineOperation(STTx const& tx, ApplyFlags flags, beast::Journ } std::uint32_t -SetSignerList::getFlagsMask(PreflightContext const& ctx) +SignerListSet::getFlagsMask(PreflightContext const& ctx) { // 0 means "Allow any flags" return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0; } NotTEC -SetSignerList::preflight(PreflightContext const& ctx) +SignerListSet::preflight(PreflightContext const& ctx) { auto const result = determineOperation(ctx.tx, ctx.flags, ctx.j); - if (std::get<0>(result) != tesSUCCESS) + if (!isTesSuccess(std::get<0>(result))) return std::get<0>(result); if (std::get<3>(result) == unknown) @@ -78,7 +78,7 @@ SetSignerList::preflight(PreflightContext const& ctx) auto const account = ctx.tx.getAccountID(sfAccount); NotTEC const ter = validateQuorumAndSignerEntries( std::get<1>(result), std::get<2>(result), account, ctx.j, ctx.rules); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) { return ter; } @@ -88,7 +88,7 @@ SetSignerList::preflight(PreflightContext const& ctx) } TER -SetSignerList::doApply() +SignerListSet::doApply() { // Perform the operation preCompute() decided on. switch (do_) @@ -103,28 +103,28 @@ SetSignerList::doApply() break; } // LCOV_EXCL_START - UNREACHABLE("xrpl::SetSignerList::doApply : invalid operation"); + UNREACHABLE("xrpl::SignerListSet::doApply : invalid operation"); return temMALFORMED; // LCOV_EXCL_STOP } void -SetSignerList::preCompute() +SignerListSet::preCompute() { // Get the quorum and operation info. auto result = determineOperation(ctx_.tx, view().flags(), j_); XRPL_ASSERT( - std::get<0>(result) == tesSUCCESS, - "xrpl::SetSignerList::preCompute : result is tesSUCCESS"); + isTesSuccess(std::get<0>(result)), + "xrpl::SignerListSet::preCompute : result is tesSUCCESS"); XRPL_ASSERT( std::get<3>(result) != unknown, - "xrpl::SetSignerList::preCompute : result is known operation"); + "xrpl::SignerListSet::preCompute : result is known operation"); quorum_ = std::get<1>(result); signers_ = std::get<2>(result); do_ = std::get<3>(result); - return Transactor::preCompute(); + Transactor::preCompute(); } // The return type is signed so it is compatible with the 3rd argument @@ -167,7 +167,7 @@ removeSignersFromLedger( { // We have to examine the current SignerList so we know how much to // reduce the OwnerCount. - SLE::pointer signers = view.peek(signerListKeylet); + SLE::pointer const signers = view.peek(signerListKeylet); // If the signer list doesn't exist we've already succeeded in deleting it. if (!signers) @@ -196,7 +196,7 @@ removeSignersFromLedger( } adjustOwnerCount( - view, view.peek(accountKeylet), removeFromOwnerCount, registry.journal("View")); + view, view.peek(accountKeylet), removeFromOwnerCount, registry.getJournal("View")); view.erase(signers); @@ -204,7 +204,7 @@ removeSignersFromLedger( } TER -SetSignerList::removeFromLedger( +SignerListSet::removeFromLedger( ServiceRegistry& registry, ApplyView& view, AccountID const& account, @@ -219,7 +219,7 @@ SetSignerList::removeFromLedger( } NotTEC -SetSignerList::validateQuorumAndSignerEntries( +SignerListSet::validateQuorumAndSignerEntries( std::uint32_t quorum, std::vector const& signers, AccountID const& account, @@ -239,7 +239,7 @@ SetSignerList::validateQuorumAndSignerEntries( // Make sure there are no duplicate signers. XRPL_ASSERT( std::is_sorted(signers.begin(), signers.end()), - "xrpl::SetSignerList::validateQuorumAndSignerEntries : sorted " + "xrpl::SignerListSet::validateQuorumAndSignerEntries : sorted " "signers"); if (std::adjacent_find(signers.begin(), signers.end()) != signers.end()) { @@ -278,7 +278,7 @@ SetSignerList::validateQuorumAndSignerEntries( } TER -SetSignerList::replaceSignerList() +SignerListSet::replaceSignerList() { auto const accountKeylet = keylet::account(account_); auto const ownerDirKeylet = keylet::ownerDir(account_); @@ -299,14 +299,14 @@ SetSignerList::replaceSignerList() std::uint32_t const oldOwnerCount{(*sle)[sfOwnerCount]}; constexpr int addedOwnerCount = 1; - std::uint32_t flags{lsfOneOwnerCount}; + std::uint32_t const flags{lsfOneOwnerCount}; XRPAmount const newReserve{view().fees().accountReserve(oldOwnerCount + addedOwnerCount)}; // We check the reserve against the starting balance because we want to // allow dipping into the reserve to pay fees. This behavior is consistent - // with CreateTicket. - if (mPriorBalance < newReserve) + // with TicketCreate. + if (preFeeBalance_ < newReserve) return tecINSUFFICIENT_RESERVE; // Everything's ducky. Add the ltSIGNER_LIST to the ledger. @@ -314,7 +314,7 @@ SetSignerList::replaceSignerList() view().insert(signerList); writeSignersToSLE(signerList, flags); - auto viewJ = ctx_.registry.journal("View"); + auto viewJ = ctx_.registry.get().getJournal("View"); // Add the signer list to the account's directory. auto const page = ctx_.view().dirInsert(ownerDirKeylet, signerListKeylet, describeOwnerDir(account_)); @@ -334,12 +334,12 @@ SetSignerList::replaceSignerList() } TER -SetSignerList::destroySignerList() +SignerListSet::destroySignerList() { auto const accountKeylet = keylet::account(account_); // Destroying the signer list is only allowed if either the master key // is enabled or there is a regular key. - SLE::pointer ledgerEntry = view().peek(accountKeylet); + SLE::pointer const ledgerEntry = view().peek(accountKeylet); if (!ledgerEntry) return tefINTERNAL; // LCOV_EXCL_LINE @@ -353,7 +353,7 @@ SetSignerList::destroySignerList() } void -SetSignerList::writeSignersToSLE(SLE::pointer const& ledgerEntry, std::uint32_t flags) const +SignerListSet::writeSignersToSLE(SLE::pointer const& ledgerEntry, std::uint32_t flags) const { // Assign the quorum, default SignerListID, and flags. if (ctx_.view().rules().enabled(fixIncludeKeyletFields)) @@ -362,7 +362,7 @@ SetSignerList::writeSignersToSLE(SLE::pointer const& ledgerEntry, std::uint32_t } ledgerEntry->setFieldU32(sfSignerQuorum, quorum_); ledgerEntry->setFieldU32(sfSignerListID, DEFAULT_SIGNER_LIST_ID); - if (flags) // Only set flags if they are non-default (default is zero). + if (flags != 0u) // Only set flags if they are non-default (default is zero). ledgerEntry->setFieldU32(sfFlags, flags); // Create the SignerListArray one SignerEntry at a time. diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index 1454bb7c77..9074122cdf 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -5,7 +5,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -108,7 +109,7 @@ checkAttestationPublicKey( if (accountFromPK == attestationSignerAccount) { // master key - if (sleAttestationSigningAccount->getFieldU32(sfFlags) & lsfDisableMaster) + if ((sleAttestationSigningAccount->getFieldU32(sfFlags) & lsfDisableMaster) != 0u) { JLOG(j.trace()) << "Attempt to add an attestation with " "disabled master key."; @@ -118,7 +119,7 @@ checkAttestationPublicKey( else { // regular key - if (std::optional regularKey = + if (std::optional const regularKey = (*sleAttestationSigningAccount)[~sfRegularKey]; regularKey != accountFromPK) { @@ -267,8 +268,9 @@ onNewAttestations( bool changed = false; for (auto att = attBegin; att != attEnd; ++att) { - if (checkAttestationPublicKey( - view, signersList, att->attestationSignerAccount, att->publicKey, j) != tesSUCCESS) + auto const ter = checkAttestationPublicKey( + view, signersList, att->attestationSignerAccount, att->publicKey, j); + if (!isTesSuccess(ter)) { // The checkAttestationPublicKey is not strictly necessary here (it // should be checked in a preclaim step), but it would be bad to let @@ -324,7 +326,8 @@ onClaim( std::unordered_map const& signersList, beast::Journal j) { - XChainClaimAttestation::MatchFields toMatch{sendingAmount, wasLockingChainSend, std::nullopt}; + XChainClaimAttestation::MatchFields const toMatch{ + sendingAmount, wasLockingChainSend, std::nullopt}; return claimHelper(attestations, view, toMatch, CheckDst::ignore, quorum, signersList, j); } @@ -337,7 +340,7 @@ enum class DepositAuthPolicy { normal, dstCanBypass }; struct TransferHelperSubmittingAccountInfo { AccountID account; - STAmount preFeeBalance; + STAmount preFeeBalance_; STAmount postFeeBalance; }; @@ -384,7 +387,7 @@ transferHelper( { // Check dst tag and deposit auth - if ((sleDst->getFlags() & lsfRequireDestTag) && !dstTag) + if (((sleDst->getFlags() & lsfRequireDestTag) != 0u) && !dstTag) return tecDST_TAG_NEEDED; // If the destination is the claim owner, and this is a claim @@ -393,7 +396,7 @@ transferHelper( bool const canBypassDepositAuth = dst == claimOwner && depositAuthPolicy == DepositAuthPolicy::dstCanBypass; - if (!canBypassDepositAuth && (sleDst->getFlags() & lsfDepositAuth) && + if (!canBypassDepositAuth && ((sleDst->getFlags() & lsfDepositAuth) != 0u) && !psb.exists(keylet::depositPreauth(dst, src))) { return tecNO_PERMISSION; @@ -416,14 +419,14 @@ transferHelper( auto const reserve = psb.fees().accountReserve(ownerCount); auto const availableBalance = [&]() -> STAmount { - STAmount const curBal = (*sleSrc)[sfBalance]; + STAmount curBal = (*sleSrc)[sfBalance]; // Checking that account == src and postFeeBalance == curBal is // not strictly necessary, but helps protect against future // changes if (!submittingAccountInfo || submittingAccountInfo->account != src || submittingAccountInfo->postFeeBalance != curBal) return curBal; - return submittingAccountInfo->preFeeBalance; + return submittingAccountInfo->preFeeBalance_; }(); if (availableBalance < amt + reserve) @@ -508,15 +511,15 @@ struct FinalizeClaimHelperResult bool isTesSuccess() const { - return mainFundsTer == tesSUCCESS && rewardTer == tesSUCCESS && - (!rmSleTer || *rmSleTer == tesSUCCESS); + return (!mainFundsTer || xrpl::isTesSuccess(*mainFundsTer)) && + (!rewardTer || xrpl::isTesSuccess(*rewardTer)) && + (!rmSleTer || xrpl::isTesSuccess(*rmSleTer)); } TER ter() const { - if ((!mainFundsTer || *mainFundsTer == tesSUCCESS) && - (!rewardTer || *rewardTer == tesSUCCESS) && (!rmSleTer || *rmSleTer == tesSUCCESS)) + if (isTesSuccess()) return tesSUCCESS; // if any phase return a tecINTERNAL or a tef, prefer returning those @@ -530,11 +533,11 @@ struct FinalizeClaimHelperResult // Only after the tecINTERNAL and tef are checked, return the first // non-success error code. - if (mainFundsTer && mainFundsTer != tesSUCCESS) + if (mainFundsTer && !xrpl::isTesSuccess(*mainFundsTer)) return *mainFundsTer; - if (rewardTer && rewardTer != tesSUCCESS) + if (rewardTer && !xrpl::isTesSuccess(*rewardTer)) return *rewardTer; - if (rmSleTer && rmSleTer != tesSUCCESS) + if (rmSleTer && !xrpl::isTesSuccess(*rmSleTer)) return *rmSleTer; return tesSUCCESS; } @@ -636,7 +639,7 @@ finalizeClaimHelper( auto const round_mode = innerSb.rules().enabled(fixXChainRewardRounding) ? Number::rounding_mode::downward : Number::getround(); - saveNumberRoundMode _{Number::setround(round_mode)}; + saveNumberRoundMode const _{Number::setround(round_mode)}; STAmount const den{rewardAccounts.size()}; return divide(rewardPool, den, rewardPool.issue()); @@ -981,7 +984,7 @@ applyCreateAccountAttestations( struct ScopeResult { OnNewAttestationResult newAttResult; - bool createCID; + bool createCID{}; XChainCreateAccountAttestations curAtts; }; @@ -1026,8 +1029,10 @@ applyCreateAccountAttestations( XChainCreateAccountAttestations curAtts = [&] { if (sleClaimID) + { return XChainCreateAccountAttestations{ sleClaimID->getFieldArray(sfXChainCreateAccountAttestations)}; + } return XChainCreateAccountAttestations{}; }(); @@ -1202,16 +1207,18 @@ attestationDoApply(ApplyContext& ctx) { auto const att = toClaim(ctx.tx); if (!att) + { // Should already be checked in preflight return tecINTERNAL; // LCOV_EXCL_LINE + } STXChainBridge const bridgeSpec = ctx.tx[sfXChainBridge]; struct ScopeResult { - STXChainBridge::ChainType srcChain; + STXChainBridge::ChainType srcChain = STXChainBridge::ChainType::locking; std::unordered_map signersList; - std::uint32_t quorum; + std::uint32_t quorum{}; AccountID thisDoor; Keylet bridgeK; }; @@ -1232,11 +1239,17 @@ attestationDoApply(ApplyContext& ctx) STXChainBridge::ChainType dstChain = STXChainBridge::ChainType::locking; { if (thisDoor == bridgeSpec.lockingChainDoor()) + { dstChain = STXChainBridge::ChainType::locking; + } else if (thisDoor == bridgeSpec.issuingChainDoor()) + { dstChain = STXChainBridge::ChainType::issuing; + } else + { return Unexpected(tecINTERNAL); + } } STXChainBridge::ChainType const srcChain = STXChainBridge::otherChain(dstChain); @@ -1391,7 +1404,7 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx) // Allowing clawing back funds would break the bridge's invariant that // wrapped funds are always backed by locked funds - if (sleIssuer->getFlags() & lsfAllowTrustLineClawback) + if ((sleIssuer->getFlags() & lsfAllowTrustLineClawback) != 0u) return tecNO_PERMISSION; } @@ -1470,7 +1483,7 @@ BridgeModify::preflight(PreflightContext const& ctx) auto const reward = ctx.tx[~sfSignatureReward]; auto const minAccountCreate = ctx.tx[~sfMinAccountCreateAmount]; auto const bridgeSpec = ctx.tx[sfXChainBridge]; - bool const clearAccountCreate = ctx.tx.getFlags() & tfClearAccountCreateAmount; + bool const clearAccountCreate = (ctx.tx.getFlags() & tfClearAccountCreateAmount) != 0u; if (!reward && !minAccountCreate && !clearAccountCreate) { @@ -1528,7 +1541,7 @@ BridgeModify::doApply() auto const bridgeSpec = ctx_.tx[sfXChainBridge]; auto const reward = ctx_.tx[~sfSignatureReward]; auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount]; - bool const clearAccountCreate = ctx_.tx.getFlags() & tfClearAccountCreateAmount; + bool const clearAccountCreate = (ctx_.tx.getFlags() & tfClearAccountCreateAmount) != 0u; auto const sleAcct = ctx_.view().peek(keylet::account(account)); if (!sleAcct) @@ -1597,11 +1610,17 @@ XChainClaim::preclaim(PreclaimContext const& ctx) bool isLockingChain = false; { if (thisDoor == bridgeSpec.lockingChainDoor()) + { isLockingChain = true; + } else if (thisDoor == bridgeSpec.issuingChainDoor()) + { isLockingChain = false; + } else + { return tecINTERNAL; // LCOV_EXCL_LINE + } } { @@ -1630,9 +1649,13 @@ XChainClaim::preclaim(PreclaimContext const& ctx) auto const otherChainAmount = [&]() -> STAmount { STAmount r(thisChainAmount); if (isLockingChain) + { r.setIssue(bridgeSpec.issuingChainIssue()); + } else + { r.setIssue(bridgeSpec.lockingChainIssue()); + } return r; }(); @@ -1695,11 +1718,17 @@ XChainClaim::doApply() STXChainBridge::ChainType dstChain = STXChainBridge::ChainType::locking; { if (thisDoor == bridgeSpec.lockingChainDoor()) + { dstChain = STXChainBridge::ChainType::locking; + } else if (thisDoor == bridgeSpec.issuingChainDoor()) + { dstChain = STXChainBridge::ChainType::issuing; + } else + { return Unexpected(tecINTERNAL); + } } STXChainBridge::ChainType const srcChain = STXChainBridge::otherChain(dstChain); @@ -1822,11 +1851,17 @@ XChainCommit::preclaim(PreclaimContext const& ctx) bool isLockingChain = false; { if (thisDoor == bridgeSpec.lockingChainDoor()) + { isLockingChain = true; + } else if (thisDoor == bridgeSpec.issuingChainDoor()) + { isLockingChain = false; + } else + { return tecINTERNAL; // LCOV_EXCL_LINE + } } if (isLockingChain) @@ -1852,7 +1887,8 @@ XChainCommit::doApply() auto const amount = ctx_.tx[sfAmount]; auto const bridgeSpec = ctx_.tx[sfXChainBridge]; - if (!psb.read(keylet::account(account))) + auto const sleAccount = psb.read(keylet::account(account)); + if (!sleAccount) return tecINTERNAL; // LCOV_EXCL_LINE auto const sleBridge = readBridge(psb, bridgeSpec); @@ -1863,7 +1899,7 @@ XChainCommit::doApply() // Support dipping into reserves to pay the fee TransferHelperSubmittingAccountInfo submittingAccountInfo{ - account_, mPriorBalance, mSourceBalance}; + account_, preFeeBalance_, (*sleAccount)[sfBalance]}; auto const thTer = transferHelper( psb, @@ -2093,11 +2129,17 @@ XChainCreateAccountCommit::preclaim(PreclaimContext const& ctx) STXChainBridge::ChainType srcChain = STXChainBridge::ChainType::locking; { if (thisDoor == bridgeSpec.lockingChainDoor()) + { srcChain = STXChainBridge::ChainType::locking; + } else if (thisDoor == bridgeSpec.issuingChainDoor()) + { srcChain = STXChainBridge::ChainType::issuing; + } else + { return tecINTERNAL; // LCOV_EXCL_LINE + } } STXChainBridge::ChainType const dstChain = STXChainBridge::otherChain(srcChain); @@ -2132,7 +2174,7 @@ XChainCreateAccountCommit::doApply() // Support dipping into reserves to pay the fee TransferHelperSubmittingAccountInfo submittingAccountInfo{ - account_, mPriorBalance, mSourceBalance}; + account_, preFeeBalance_, (*sle)[sfBalance]}; STAmount const toTransfer = amount + reward; auto const thTer = transferHelper( psb, diff --git a/src/libxrpl/tx/transactors/check/CancelCheck.cpp b/src/libxrpl/tx/transactors/check/CheckCancel.cpp similarity index 84% rename from src/libxrpl/tx/transactors/check/CancelCheck.cpp rename to src/libxrpl/tx/transactors/check/CheckCancel.cpp index 645327a4c5..be3b434fb6 100644 --- a/src/libxrpl/tx/transactors/check/CancelCheck.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCancel.cpp @@ -1,22 +1,22 @@ -#include #include #include +#include #include #include #include #include -#include +#include namespace xrpl { NotTEC -CancelCheck::preflight(PreflightContext const& ctx) +CheckCancel::preflight(PreflightContext const& ctx) { return tesSUCCESS; } TER -CancelCheck::preclaim(PreclaimContext const& ctx) +CheckCancel::preclaim(PreclaimContext const& ctx) { auto const sleCheck = ctx.view.read(keylet::check(ctx.tx[sfCheckID])); if (!sleCheck) @@ -25,15 +25,11 @@ CancelCheck::preclaim(PreclaimContext const& ctx) return tecNO_ENTRY; } - using duration = NetClock::duration; - using timepoint = NetClock::time_point; - auto const optExpiry = (*sleCheck)[~sfExpiration]; - // Expiration is defined in terms of the close time of the parent // ledger, because we definitively know the time that it closed but // we do not know the closing time of the ledger that is under // construction. - if (!optExpiry || (ctx.view.parentCloseTime() < timepoint{duration{*optExpiry}})) + if (!hasExpired(ctx.view, (*sleCheck)[~sfExpiration])) { // If the check is not yet expired, then only the creator or the // destination may cancel the check. @@ -49,7 +45,7 @@ CancelCheck::preclaim(PreclaimContext const& ctx) } TER -CancelCheck::doApply() +CheckCancel::doApply() { auto const sleCheck = view().peek(keylet::check(ctx_.tx[sfCheckID])); if (!sleCheck) @@ -61,7 +57,7 @@ CancelCheck::doApply() AccountID const srcId{sleCheck->getAccountID(sfAccount)}; AccountID const dstId{sleCheck->getAccountID(sfDestination)}; - auto viewJ = ctx_.registry.journal("View"); + auto viewJ = ctx_.registry.get().getJournal("View"); // If the check is not written to self (and it shouldn't be), remove the // check from the destination account root. diff --git a/src/libxrpl/tx/transactors/check/CashCheck.cpp b/src/libxrpl/tx/transactors/check/CheckCash.cpp similarity index 88% rename from src/libxrpl/tx/transactors/check/CashCheck.cpp rename to src/libxrpl/tx/transactors/check/CheckCash.cpp index 1628b284a9..fa908c3aa3 100644 --- a/src/libxrpl/tx/transactors/check/CashCheck.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCash.cpp @@ -1,19 +1,20 @@ -#include #include #include +#include +#include #include #include #include #include #include -#include +#include #include namespace xrpl { NotTEC -CashCheck::preflight(PreflightContext const& ctx) +CheckCash::preflight(PreflightContext const& ctx) { // Exactly one of Amount or DeliverMin must be present. auto const optAmount = ctx.tx[~sfAmount]; @@ -44,7 +45,7 @@ CashCheck::preflight(PreflightContext const& ctx) } TER -CashCheck::preclaim(PreclaimContext const& ctx) +CheckCash::preclaim(PreclaimContext const& ctx) { auto const sleCheck = ctx.view.read(keylet::check(ctx.tx[sfCheckID])); if (!sleCheck) @@ -80,7 +81,8 @@ CashCheck::preclaim(PreclaimContext const& ctx) return tecNO_ENTRY; } - if ((sleDst->getFlags() & lsfRequireDestTag) && !sleCheck->isFieldPresent(sfDestinationTag)) + if (((sleDst->getFlags() & lsfRequireDestTag) != 0u) && + !sleCheck->isFieldPresent(sfDestinationTag)) { // The tag is basically account-specific information we don't // understand, but we can require someone to fill it in. @@ -153,7 +155,7 @@ CashCheck::preclaim(PreclaimContext const& ctx) return tecNO_ISSUER; } - if (sleIssuer->at(sfFlags) & lsfRequireAuth) + if ((sleIssuer->at(sfFlags) & lsfRequireAuth) != 0u) { auto const sleTrustLine = ctx.view.read(keylet::line(dstId, issuerId, currency)); @@ -170,7 +172,7 @@ CashCheck::preclaim(PreclaimContext const& ctx) bool const canonical_gt(dstId > issuerId); bool const is_authorized( - sleTrustLine->at(sfFlags) & (canonical_gt ? lsfLowAuth : lsfHighAuth)); + (sleTrustLine->at(sfFlags) & (canonical_gt ? lsfLowAuth : lsfHighAuth)) != 0u); if (!is_authorized) { @@ -196,7 +198,7 @@ CashCheck::preclaim(PreclaimContext const& ctx) } TER -CashCheck::doApply() +CheckCash::doApply() { // Flow requires that we operate on a PaymentSandbox, rather than // directly on a View. @@ -230,7 +232,7 @@ CashCheck::doApply() // // If it is not a check to self (as should be the case), then there's // work to do... - auto viewJ = ctx_.registry.journal("View"); + auto viewJ = ctx_.registry.get().getJournal("View"); auto const optDeliverMin = ctx_.tx[~sfDeliverMin]; if (srcId != account_) @@ -265,12 +267,14 @@ CashCheck::doApply() } if (optDeliverMin) + { // Set the DeliveredAmount metadata. ctx_.deliver(xrpDeliver); + } // The source account has enough XRP so make the ledger change. if (TER const ter{transferXRP(psb, srcId, account_, xrpDeliver, viewJ)}; - ter != tesSUCCESS) + !isTesSuccess(ter)) { // The transfer failed. Return the error code. return ter; @@ -309,7 +313,7 @@ CashCheck::doApply() // Can the account cover the trust line's reserve? if (std::uint32_t const ownerCount = {sleDst->at(sfOwnerCount)}; - mPriorBalance < psb.fees().accountReserve(ownerCount + 1)) + preFeeBalance_ < psb.fees().accountReserve(ownerCount + 1)) { JLOG(j_.trace()) << "Trust line does not exist. " "Insufficent reserve to create line."; @@ -321,28 +325,26 @@ CashCheck::doApply() STAmount initialBalance(flowDeliver.issue()); initialBalance.setIssuer(noAccount()); - // clang-format off if (TER const ter = trustCreate( - psb, // payment sandbox - destLow, // is dest low? - issuer, // source - account_, // destination - trustLineKey.key, // ledger index - sleDst, // Account to add to - false, // authorize account - (sleDst->getFlags() & lsfDefaultRipple) == 0, - false, // freeze trust line - false, // deep freeze trust line - initialBalance, // zero initial balance - Issue(currency, account_), // limit of zero - 0, // quality in - 0, // quality out - viewJ); // journal + psb, // payment sandbox + destLow, // is dest low? + issuer, // source + account_, // destination + trustLineKey.key, // ledger index + sleDst, // Account to add to + false, // authorize account + (sleDst->getFlags() & lsfDefaultRipple) == 0, // + false, // freeze trust line + false, // deep freeze trust line + initialBalance, // zero initial balance + Issue(currency, account_), // limit of zero + 0, // quality in + 0, // quality out + viewJ); // journal !isTesSuccess(ter)) { return ter; } - // clang-format on psb.update(sleDst); @@ -363,7 +365,7 @@ CashCheck::doApply() STAmount const savedLimit = sleTrustLine->at(tweakedLimit); // Make sure the tweaked limits are restored when we leave scope. - scope_exit fixup([&psb, &trustLineKey, &tweakedLimit, &savedLimit]() { + scope_exit const fixup([&psb, &trustLineKey, &tweakedLimit, &savedLimit]() { if (auto const sleTrustLine = psb.peek(trustLineKey)) sleTrustLine->at(tweakedLimit) = savedLimit; }); @@ -389,7 +391,7 @@ CashCheck::doApply() std::nullopt, // check does not support domain viewJ); - if (result.result() != tesSUCCESS) + if (!isTesSuccess(result.result())) { JLOG(ctx_.journal.warn()) << "flow failed when cashing check."; return result.result(); diff --git a/src/libxrpl/tx/transactors/check/CreateCheck.cpp b/src/libxrpl/tx/transactors/check/CheckCreate.cpp similarity index 92% rename from src/libxrpl/tx/transactors/check/CreateCheck.cpp rename to src/libxrpl/tx/transactors/check/CheckCreate.cpp index 9e883e7fbb..fde3373691 100644 --- a/src/libxrpl/tx/transactors/check/CreateCheck.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCreate.cpp @@ -1,15 +1,17 @@ -#include #include +#include +#include +#include #include #include #include #include -#include +#include namespace xrpl { NotTEC -CreateCheck::preflight(PreflightContext const& ctx) +CheckCreate::preflight(PreflightContext const& ctx) { if (ctx.tx[sfAccount] == ctx.tx[sfDestination]) { @@ -47,7 +49,7 @@ CreateCheck::preflight(PreflightContext const& ctx) } TER -CreateCheck::preclaim(PreclaimContext const& ctx) +CheckCreate::preclaim(PreclaimContext const& ctx) { AccountID const dstId{ctx.tx[sfDestination]}; auto const sleDst = ctx.view.read(keylet::account(dstId)); @@ -60,7 +62,7 @@ CreateCheck::preclaim(PreclaimContext const& ctx) auto const flags = sleDst->getFlags(); // Check if the destination has disallowed incoming checks - if (flags & lsfDisallowIncomingCheck) + if ((flags & lsfDisallowIncomingCheck) != 0u) return tecNO_PERMISSION; // Pseudo-accounts cannot cash checks. Note, this is not amendment-gated @@ -70,7 +72,7 @@ CreateCheck::preclaim(PreclaimContext const& ctx) if (isPseudoAccount(sleDst)) return tecNO_PERMISSION; - if ((flags & lsfRequireDestTag) && !ctx.tx.isFieldPresent(sfDestinationTag)) + if (((flags & lsfRequireDestTag) != 0u) && !ctx.tx.isFieldPresent(sfDestinationTag)) { // The tag is basically account-specific information we don't // understand, but we can require someone to fill it in. @@ -128,7 +130,7 @@ CreateCheck::preclaim(PreclaimContext const& ctx) } TER -CreateCheck::doApply() +CheckCreate::doApply() { auto const sle = view().peek(keylet::account(account_)); if (!sle) @@ -140,7 +142,7 @@ CreateCheck::doApply() { STAmount const reserve{view().fees().accountReserve(sle->getFieldU32(sfOwnerCount) + 1)}; - if (mPriorBalance < reserve) + if (preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; } @@ -166,7 +168,7 @@ CreateCheck::doApply() view().insert(sleCheck); - auto viewJ = ctx_.registry.journal("View"); + auto viewJ = ctx_.registry.get().getJournal("View"); // If it's not a self-send (and it shouldn't be), add Check to the // destination's owner directory. if (dstAccountId != account_) diff --git a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp new file mode 100644 index 0000000000..9c0018a647 --- /dev/null +++ b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +using namespace credentials; + +std::uint32_t +CredentialAccept::getFlagsMask(PreflightContext const& ctx) +{ + // 0 means "Allow any flags" + return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0; +} + +NotTEC +CredentialAccept::preflight(PreflightContext const& ctx) +{ + if (!ctx.tx[sfIssuer]) + { + JLOG(ctx.j.trace()) << "Malformed transaction: Issuer field zeroed."; + return temINVALID_ACCOUNT_ID; + } + + auto const credType = ctx.tx[sfCredentialType]; + if (credType.empty() || (credType.size() > maxCredentialTypeLength)) + { + JLOG(ctx.j.trace()) << "Malformed transaction: invalid size of CredentialType."; + return temMALFORMED; + } + + return tesSUCCESS; +} + +TER +CredentialAccept::preclaim(PreclaimContext const& ctx) +{ + AccountID const subject = ctx.tx[sfAccount]; + AccountID const issuer = ctx.tx[sfIssuer]; + auto const credType(ctx.tx[sfCredentialType]); + + if (!ctx.view.exists(keylet::account(issuer))) + { + JLOG(ctx.j.warn()) << "No issuer: " << to_string(issuer); + return tecNO_ISSUER; + } + + auto const sleCred = ctx.view.read(keylet::credential(subject, issuer, credType)); + if (!sleCred) + { + JLOG(ctx.j.warn()) << "No credential: " << to_string(subject) << ", " << to_string(issuer) + << ", " << credType; + return tecNO_ENTRY; + } + + if ((sleCred->getFieldU32(sfFlags) & lsfAccepted) != 0u) + { + JLOG(ctx.j.warn()) << "Credential already accepted: " << to_string(subject) << ", " + << to_string(issuer) << ", " << credType; + return tecDUPLICATE; + } + + return tesSUCCESS; +} + +TER +CredentialAccept::doApply() +{ + AccountID const issuer{ctx_.tx[sfIssuer]}; + + // Both exist as credential object exist itself (checked in preclaim) + auto const sleSubject = view().peek(keylet::account(account_)); + auto const sleIssuer = view().peek(keylet::account(issuer)); + + if (!sleSubject || !sleIssuer) + return tefINTERNAL; // LCOV_EXCL_LINE + + { + STAmount const reserve{ + view().fees().accountReserve(sleSubject->getFieldU32(sfOwnerCount) + 1)}; + if (preFeeBalance_ < reserve) + return tecINSUFFICIENT_RESERVE; + } + + auto const credType(ctx_.tx[sfCredentialType]); + Keylet const credentialKey = keylet::credential(account_, issuer, credType); + auto const sleCred = view().peek(credentialKey); // Checked in preclaim() + + if (checkExpired(sleCred, view().header().parentCloseTime)) + { + JLOG(j_.trace()) << "Credential is expired: " << sleCred->getText(); + // delete expired credentials even if the transaction failed + auto const err = credentials::deleteSLE(view(), sleCred, j_); + return isTesSuccess(err) ? tecEXPIRED : err; + } + + sleCred->setFieldU32(sfFlags, lsfAccepted); + view().update(sleCred); + + adjustOwnerCount(view(), sleIssuer, -1, j_); + adjustOwnerCount(view(), sleSubject, 1, j_); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp new file mode 100644 index 0000000000..f8f0f01b63 --- /dev/null +++ b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +/* + Credentials + ====== + + A verifiable credentials (VC + https://en.wikipedia.org/wiki/Verifiable_credentials), as defined by the W3C + specification (https://www.w3.org/TR/vc-data-model-2.0/), is a + secure and tamper-evident way to represent information about a subject, such + as an individual, organization, or even an IoT device. These credentials are + issued by a trusted entity and can be verified by third parties without + directly involving the issuer at all. +*/ + +using namespace credentials; + +std::uint32_t +CredentialCreate::getFlagsMask(PreflightContext const& ctx) +{ + // 0 means "Allow any flags" + return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0; +} + +NotTEC +CredentialCreate::preflight(PreflightContext const& ctx) +{ + auto const& tx = ctx.tx; + auto& j = ctx.j; + + if (!tx[sfSubject]) + { + JLOG(j.trace()) << "Malformed transaction: Invalid Subject"; + return temMALFORMED; + } + + auto const uri = tx[~sfURI]; + if (uri && (uri->empty() || (uri->size() > maxCredentialURILength))) + { + JLOG(j.trace()) << "Malformed transaction: invalid size of URI."; + return temMALFORMED; + } + + auto const credType = tx[sfCredentialType]; + if (credType.empty() || (credType.size() > maxCredentialTypeLength)) + { + JLOG(j.trace()) << "Malformed transaction: invalid size of CredentialType."; + return temMALFORMED; + } + + return tesSUCCESS; +} + +TER +CredentialCreate::preclaim(PreclaimContext const& ctx) +{ + auto const credType(ctx.tx[sfCredentialType]); + auto const subject = ctx.tx[sfSubject]; + + if (!ctx.view.exists(keylet::account(subject))) + { + JLOG(ctx.j.trace()) << "Subject doesn't exist."; + return tecNO_TARGET; + } + + if (ctx.view.exists(keylet::credential(subject, ctx.tx[sfAccount], credType))) + { + JLOG(ctx.j.trace()) << "Credential already exists."; + return tecDUPLICATE; + } + + return tesSUCCESS; +} + +TER +CredentialCreate::doApply() +{ + auto const subject = ctx_.tx[sfSubject]; + auto const credType(ctx_.tx[sfCredentialType]); + Keylet const credentialKey = keylet::credential(subject, account_, credType); + + auto const sleCred = std::make_shared(credentialKey); + if (!sleCred) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const optExp = ctx_.tx[~sfExpiration]; + if (optExp) + { + std::uint32_t const closeTime = + ctx_.view().header().parentCloseTime.time_since_epoch().count(); + + if (closeTime > *optExp) + { + JLOG(j_.trace()) << "Malformed transaction: " + "Expiration time is in the past."; + return tecEXPIRED; + } + + sleCred->setFieldU32(sfExpiration, *optExp); + } + + auto const sleIssuer = view().peek(keylet::account(account_)); + if (!sleIssuer) + return tefINTERNAL; // LCOV_EXCL_LINE + + { + STAmount const reserve{ + view().fees().accountReserve(sleIssuer->getFieldU32(sfOwnerCount) + 1)}; + if (preFeeBalance_ < reserve) + return tecINSUFFICIENT_RESERVE; + } + + sleCred->setAccountID(sfSubject, subject); + sleCred->setAccountID(sfIssuer, account_); + sleCred->setFieldVL(sfCredentialType, credType); + + if (ctx_.tx.isFieldPresent(sfURI)) + sleCred->setFieldVL(sfURI, ctx_.tx.getFieldVL(sfURI)); + + { + auto const page = + view().dirInsert(keylet::ownerDir(account_), credentialKey, describeOwnerDir(account_)); + JLOG(j_.trace()) << "Adding Credential to owner directory " << to_string(credentialKey.key) + << ": " << (page ? "success" : "failure"); + if (!page) + return tecDIR_FULL; + sleCred->setFieldU64(sfIssuerNode, *page); + + adjustOwnerCount(view(), sleIssuer, 1, j_); + } + + if (subject == account_) + { + sleCred->setFieldU32(sfFlags, lsfAccepted); + } + else + { + // Added to both dirs, owned only by issuer. CredentialAccept will transfer ownership to + // subject. CredentialDelete will remove from both dirs and decrement 1 ownerCount. + auto const page = + view().dirInsert(keylet::ownerDir(subject), credentialKey, describeOwnerDir(subject)); + JLOG(j_.trace()) << "Adding Credential to subject directory " + << to_string(credentialKey.key) << ": " << (page ? "success" : "failure"); + if (!page) + return tecDIR_FULL; + sleCred->setFieldU64(sfSubjectNode, *page); + } + + view().insert(sleCred); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp b/src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp new file mode 100644 index 0000000000..5f78ba6757 --- /dev/null +++ b/src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +using namespace credentials; + +std::uint32_t +CredentialDelete::getFlagsMask(PreflightContext const& ctx) +{ + // 0 means "Allow any flags" + return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0; +} + +NotTEC +CredentialDelete::preflight(PreflightContext const& ctx) +{ + auto const subject = ctx.tx[~sfSubject]; + auto const issuer = ctx.tx[~sfIssuer]; + + if (!subject && !issuer) + { + // Neither field is present, the transaction is malformed. + JLOG(ctx.j.trace()) << "Malformed transaction: " + "No Subject or Issuer fields."; + return temMALFORMED; + } + + // Make sure that the passed account is valid. + if ((subject && subject->isZero()) || (issuer && issuer->isZero())) + { + JLOG(ctx.j.trace()) << "Malformed transaction: Subject or Issuer " + "field zeroed."; + return temINVALID_ACCOUNT_ID; + } + + auto const credType = ctx.tx[sfCredentialType]; + if (credType.empty() || (credType.size() > maxCredentialTypeLength)) + { + JLOG(ctx.j.trace()) << "Malformed transaction: invalid size of CredentialType."; + return temMALFORMED; + } + + return tesSUCCESS; +} + +TER +CredentialDelete::preclaim(PreclaimContext const& ctx) +{ + AccountID const account{ctx.tx[sfAccount]}; + auto const subject = ctx.tx[~sfSubject].value_or(account); + auto const issuer = ctx.tx[~sfIssuer].value_or(account); + auto const credType(ctx.tx[sfCredentialType]); + + if (!ctx.view.exists(keylet::credential(subject, issuer, credType))) + return tecNO_ENTRY; + + return tesSUCCESS; +} + +TER +CredentialDelete::doApply() +{ + auto const subject = ctx_.tx[~sfSubject].value_or(account_); + auto const issuer = ctx_.tx[~sfIssuer].value_or(account_); + + auto const credType(ctx_.tx[sfCredentialType]); + auto const sleCred = view().peek(keylet::credential(subject, issuer, credType)); + if (!sleCred) + return tefINTERNAL; // LCOV_EXCL_LINE + + if ((subject != account_) && (issuer != account_) && + !checkExpired(sleCred, ctx_.view().header().parentCloseTime)) + { + JLOG(j_.trace()) << "Can't delete non-expired credential."; + return tecNO_PERMISSION; + } + + return deleteSLE(view(), sleCred, j_); +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/credentials/Credentials.cpp b/src/libxrpl/tx/transactors/credentials/Credentials.cpp deleted file mode 100644 index 079b75a9fe..0000000000 --- a/src/libxrpl/tx/transactors/credentials/Credentials.cpp +++ /dev/null @@ -1,341 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace xrpl { - -/* - Credentials - ====== - - A verifiable credentials (VC - https://en.wikipedia.org/wiki/Verifiable_credentials), as defined by the W3C - specification (https://www.w3.org/TR/vc-data-model-2.0/), is a - secure and tamper-evident way to represent information about a subject, such - as an individual, organization, or even an IoT device. These credentials are - issued by a trusted entity and can be verified by third parties without - directly involving the issuer at all. -*/ - -using namespace credentials; - -// ------- CREATE -------------------------- - -std::uint32_t -CredentialCreate::getFlagsMask(PreflightContext const& ctx) -{ - // 0 means "Allow any flags" - return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0; -} - -NotTEC -CredentialCreate::preflight(PreflightContext const& ctx) -{ - auto const& tx = ctx.tx; - auto& j = ctx.j; - - if (!tx[sfSubject]) - { - JLOG(j.trace()) << "Malformed transaction: Invalid Subject"; - return temMALFORMED; - } - - auto const uri = tx[~sfURI]; - if (uri && (uri->empty() || (uri->size() > maxCredentialURILength))) - { - JLOG(j.trace()) << "Malformed transaction: invalid size of URI."; - return temMALFORMED; - } - - auto const credType = tx[sfCredentialType]; - if (credType.empty() || (credType.size() > maxCredentialTypeLength)) - { - JLOG(j.trace()) << "Malformed transaction: invalid size of CredentialType."; - return temMALFORMED; - } - - return tesSUCCESS; -} - -TER -CredentialCreate::preclaim(PreclaimContext const& ctx) -{ - auto const credType(ctx.tx[sfCredentialType]); - auto const subject = ctx.tx[sfSubject]; - - if (!ctx.view.exists(keylet::account(subject))) - { - JLOG(ctx.j.trace()) << "Subject doesn't exist."; - return tecNO_TARGET; - } - - if (ctx.view.exists(keylet::credential(subject, ctx.tx[sfAccount], credType))) - { - JLOG(ctx.j.trace()) << "Credential already exists."; - return tecDUPLICATE; - } - - return tesSUCCESS; -} - -TER -CredentialCreate::doApply() -{ - auto const subject = ctx_.tx[sfSubject]; - auto const credType(ctx_.tx[sfCredentialType]); - Keylet const credentialKey = keylet::credential(subject, account_, credType); - - auto const sleCred = std::make_shared(credentialKey); - if (!sleCred) - return tefINTERNAL; // LCOV_EXCL_LINE - - auto const optExp = ctx_.tx[~sfExpiration]; - if (optExp) - { - std::uint32_t const closeTime = - ctx_.view().header().parentCloseTime.time_since_epoch().count(); - - if (closeTime > *optExp) - { - JLOG(j_.trace()) << "Malformed transaction: " - "Expiration time is in the past."; - return tecEXPIRED; - } - - sleCred->setFieldU32(sfExpiration, ctx_.tx.getFieldU32(sfExpiration)); - } - - auto const sleIssuer = view().peek(keylet::account(account_)); - if (!sleIssuer) - return tefINTERNAL; // LCOV_EXCL_LINE - - { - STAmount const reserve{ - view().fees().accountReserve(sleIssuer->getFieldU32(sfOwnerCount) + 1)}; - if (mPriorBalance < reserve) - return tecINSUFFICIENT_RESERVE; - } - - sleCred->setAccountID(sfSubject, subject); - sleCred->setAccountID(sfIssuer, account_); - sleCred->setFieldVL(sfCredentialType, credType); - - if (ctx_.tx.isFieldPresent(sfURI)) - sleCred->setFieldVL(sfURI, ctx_.tx.getFieldVL(sfURI)); - - { - auto const page = - view().dirInsert(keylet::ownerDir(account_), credentialKey, describeOwnerDir(account_)); - JLOG(j_.trace()) << "Adding Credential to owner directory " << to_string(credentialKey.key) - << ": " << (page ? "success" : "failure"); - if (!page) - return tecDIR_FULL; - sleCred->setFieldU64(sfIssuerNode, *page); - - adjustOwnerCount(view(), sleIssuer, 1, j_); - } - - if (subject == account_) - { - sleCred->setFieldU32(sfFlags, lsfAccepted); - } - else - { - auto const page = - view().dirInsert(keylet::ownerDir(subject), credentialKey, describeOwnerDir(subject)); - JLOG(j_.trace()) << "Adding Credential to owner directory " << to_string(credentialKey.key) - << ": " << (page ? "success" : "failure"); - if (!page) - return tecDIR_FULL; - sleCred->setFieldU64(sfSubjectNode, *page); - view().update(view().peek(keylet::account(subject))); - } - - view().insert(sleCred); - - return tesSUCCESS; -} - -// ------- DELETE -------------------------- - -std::uint32_t -CredentialDelete::getFlagsMask(PreflightContext const& ctx) -{ - // 0 means "Allow any flags" - return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0; -} - -NotTEC -CredentialDelete::preflight(PreflightContext const& ctx) -{ - auto const subject = ctx.tx[~sfSubject]; - auto const issuer = ctx.tx[~sfIssuer]; - - if (!subject && !issuer) - { - // Neither field is present, the transaction is malformed. - JLOG(ctx.j.trace()) << "Malformed transaction: " - "No Subject or Issuer fields."; - return temMALFORMED; - } - - // Make sure that the passed account is valid. - if ((subject && subject->isZero()) || (issuer && issuer->isZero())) - { - JLOG(ctx.j.trace()) << "Malformed transaction: Subject or Issuer " - "field zeroed."; - return temINVALID_ACCOUNT_ID; - } - - auto const credType = ctx.tx[sfCredentialType]; - if (credType.empty() || (credType.size() > maxCredentialTypeLength)) - { - JLOG(ctx.j.trace()) << "Malformed transaction: invalid size of CredentialType."; - return temMALFORMED; - } - - return tesSUCCESS; -} - -TER -CredentialDelete::preclaim(PreclaimContext const& ctx) -{ - AccountID const account{ctx.tx[sfAccount]}; - auto const subject = ctx.tx[~sfSubject].value_or(account); - auto const issuer = ctx.tx[~sfIssuer].value_or(account); - auto const credType(ctx.tx[sfCredentialType]); - - if (!ctx.view.exists(keylet::credential(subject, issuer, credType))) - return tecNO_ENTRY; - - return tesSUCCESS; -} - -TER -CredentialDelete::doApply() -{ - auto const subject = ctx_.tx[~sfSubject].value_or(account_); - auto const issuer = ctx_.tx[~sfIssuer].value_or(account_); - - auto const credType(ctx_.tx[sfCredentialType]); - auto const sleCred = view().peek(keylet::credential(subject, issuer, credType)); - if (!sleCred) - return tefINTERNAL; // LCOV_EXCL_LINE - - if ((subject != account_) && (issuer != account_) && - !checkExpired(sleCred, ctx_.view().header().parentCloseTime)) - { - JLOG(j_.trace()) << "Can't delete non-expired credential."; - return tecNO_PERMISSION; - } - - return deleteSLE(view(), sleCred, j_); -} - -// ------- APPLY -------------------------- - -std::uint32_t -CredentialAccept::getFlagsMask(PreflightContext const& ctx) -{ - // 0 means "Allow any flags" - return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0; -} - -NotTEC -CredentialAccept::preflight(PreflightContext const& ctx) -{ - if (!ctx.tx[sfIssuer]) - { - JLOG(ctx.j.trace()) << "Malformed transaction: Issuer field zeroed."; - return temINVALID_ACCOUNT_ID; - } - - auto const credType = ctx.tx[sfCredentialType]; - if (credType.empty() || (credType.size() > maxCredentialTypeLength)) - { - JLOG(ctx.j.trace()) << "Malformed transaction: invalid size of CredentialType."; - return temMALFORMED; - } - - return tesSUCCESS; -} - -TER -CredentialAccept::preclaim(PreclaimContext const& ctx) -{ - AccountID const subject = ctx.tx[sfAccount]; - AccountID const issuer = ctx.tx[sfIssuer]; - auto const credType(ctx.tx[sfCredentialType]); - - if (!ctx.view.exists(keylet::account(issuer))) - { - JLOG(ctx.j.warn()) << "No issuer: " << to_string(issuer); - return tecNO_ISSUER; - } - - auto const sleCred = ctx.view.read(keylet::credential(subject, issuer, credType)); - if (!sleCred) - { - JLOG(ctx.j.warn()) << "No credential: " << to_string(subject) << ", " << to_string(issuer) - << ", " << credType; - return tecNO_ENTRY; - } - - if (sleCred->getFieldU32(sfFlags) & lsfAccepted) - { - JLOG(ctx.j.warn()) << "Credential already accepted: " << to_string(subject) << ", " - << to_string(issuer) << ", " << credType; - return tecDUPLICATE; - } - - return tesSUCCESS; -} - -TER -CredentialAccept::doApply() -{ - AccountID const issuer{ctx_.tx[sfIssuer]}; - - // Both exist as credential object exist itself (checked in preclaim) - auto const sleSubject = view().peek(keylet::account(account_)); - auto const sleIssuer = view().peek(keylet::account(issuer)); - - if (!sleSubject || !sleIssuer) - return tefINTERNAL; // LCOV_EXCL_LINE - - { - STAmount const reserve{ - view().fees().accountReserve(sleSubject->getFieldU32(sfOwnerCount) + 1)}; - if (mPriorBalance < reserve) - return tecINSUFFICIENT_RESERVE; - } - - auto const credType(ctx_.tx[sfCredentialType]); - Keylet const credentialKey = keylet::credential(account_, issuer, credType); - auto const sleCred = view().peek(credentialKey); // Checked in preclaim() - - if (checkExpired(sleCred, view().header().parentCloseTime)) - { - JLOG(j_.trace()) << "Credential is expired: " << sleCred->getText(); - // delete expired credentials even if the transaction failed - auto const err = credentials::deleteSLE(view(), sleCred, j_); - return isTesSuccess(err) ? tecEXPIRED : err; - } - - sleCred->setFieldU32(sfFlags, lsfAccepted); - view().update(sleCred); - - adjustOwnerCount(view(), sleIssuer, -1, j_); - adjustOwnerCount(view(), sleSubject, 1, j_); - - return tesSUCCESS; -} - -} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp index afb84c1c1e..9fb17c4d1f 100644 --- a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include #include @@ -41,6 +43,13 @@ DelegateSet::preclaim(PreclaimContext const& ctx) if (!ctx.view.exists(keylet::account(ctx.tx[sfAuthorize]))) return tecNO_TARGET; + // Deleting the delegate object is invalid if it doesn’t exist. + if (ctx.tx.getFieldArray(sfPermissions).empty() && + !ctx.view.exists(keylet::delegate(ctx.tx[sfAccount], ctx.tx[sfAuthorize]))) + { + return tecNO_ENTRY; + } + return tesSUCCESS; } @@ -59,38 +68,40 @@ DelegateSet::doApply() { auto const& permissions = ctx_.tx.getFieldArray(sfPermissions); if (permissions.empty()) + { // if permissions array is empty, delete the ledger object. return deleteDelegate(view(), sle, account_, j_); + } sle->setFieldArray(sfPermissions, permissions); ctx_.view().update(sle); return tesSUCCESS; } + auto const& permissions = ctx_.tx.getFieldArray(sfPermissions); + if (permissions.empty()) + return tecINTERNAL; // LCOV_EXCL_LINE + STAmount const reserve{ ctx_.view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; - if (mPriorBalance < reserve) + if (preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; - auto const& permissions = ctx_.tx.getFieldArray(sfPermissions); - if (!permissions.empty()) - { - sle = std::make_shared(delegateKey); - sle->setAccountID(sfAccount, account_); - sle->setAccountID(sfAuthorize, authAccount); + sle = std::make_shared(delegateKey); + sle->setAccountID(sfAccount, account_); + sle->setAccountID(sfAuthorize, authAccount); - sle->setFieldArray(sfPermissions, permissions); - auto const page = ctx_.view().dirInsert( - keylet::ownerDir(account_), delegateKey, describeOwnerDir(account_)); + sle->setFieldArray(sfPermissions, permissions); + auto const page = + ctx_.view().dirInsert(keylet::ownerDir(account_), delegateKey, describeOwnerDir(account_)); - if (!page) - return tecDIR_FULL; // LCOV_EXCL_LINE + if (!page) + return tecDIR_FULL; // LCOV_EXCL_LINE - (*sle)[sfOwnerNode] = *page; - ctx_.view().insert(sle); - adjustOwnerCount(ctx_.view(), sleOwner, 1, ctx_.journal); - } + (*sle)[sfOwnerNode] = *page; + ctx_.view().insert(sle); + adjustOwnerCount(ctx_.view(), sleOwner, 1, ctx_.journal); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp b/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp index d9d74a1212..862fcf280c 100644 --- a/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp @@ -1,5 +1,5 @@ +#include #include -#include namespace xrpl { NotTEC diff --git a/src/libxrpl/tx/transactors/dex/AMMBid.cpp b/src/libxrpl/tx/transactors/dex/AMMBid.cpp index d73e65ec3a..805b5073e4 100644 --- a/src/libxrpl/tx/transactors/dex/AMMBid.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMBid.cpp @@ -1,12 +1,12 @@ #include #include +#include +#include #include #include #include #include #include -#include -#include namespace xrpl { @@ -46,15 +46,15 @@ AMMBid::preflight(PreflightContext const& ctx) if (ctx.tx.isFieldPresent(sfAuthAccounts)) { - if (auto const authAccounts = ctx.tx.getFieldArray(sfAuthAccounts); - authAccounts.size() > AUCTION_SLOT_MAX_AUTH_ACCOUNTS) + auto const authAccounts = ctx.tx.getFieldArray(sfAuthAccounts); + if (authAccounts.size() > AUCTION_SLOT_MAX_AUTH_ACCOUNTS) { JLOG(ctx.j.debug()) << "AMM Bid: Invalid number of AuthAccounts."; return temMALFORMED; } - else if (ctx.rules.enabled(fixAMMv1_3)) + if (ctx.rules.enabled(fixAMMv1_3)) { - AccountID account = ctx.tx[sfAccount]; + AccountID const account = ctx.tx[sfAccount]; std::set unique; for (auto const& obj : authAccounts) { @@ -193,14 +193,22 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour auctionSlot.setAccountID(sfAccount, account_); auctionSlot.setFieldU32(sfExpiration, current + TOTAL_TIME_SLOT_SECS); if (fee != 0) + { auctionSlot.setFieldU16(sfDiscountedFee, fee); + } else if (auctionSlot.isFieldPresent(sfDiscountedFee)) + { auctionSlot.makeFieldAbsent(sfDiscountedFee); + } auctionSlot.setFieldAmount(sfPrice, toSTAmount(lpTokens.issue(), minPrice)); if (ctx_.tx.isFieldPresent(sfAuthAccounts)) + { auctionSlot.setFieldArray(sfAuthAccounts, ctx_.tx.getFieldArray(sfAuthAccounts)); + } else + { auctionSlot.makeFieldAbsent(sfAuthAccounts); + } // Burn the remaining bid amount auto const saBurn = adjustLPTokens(lptAMMBalance, toSTAmount(lptAMMBalance.issue(), burn), IsDeposit::No); @@ -214,7 +222,7 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour // LCOV_EXCL_STOP } auto res = redeemIOU(sb, account_, saBurn, lpTokens.issue(), ctx_.journal); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(ctx_.journal.debug()) << "AMM Bid: failed to redeem."; return res; @@ -245,7 +253,7 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour { return std::max(computedPrice, Number(*bidMin)); } - else if (bidMax) + if (bidMax) { if (computedPrice <= *bidMax) return computedPrice; @@ -253,23 +261,30 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour << "AMM Bid: not in range " << computedPrice << " " << *bidMax; return std::nullopt; } - else - return computedPrice; + + return computedPrice; }(); if (!payPrice) + { return Unexpected(tecAMM_FAILED); - else if (payPrice > lpTokens) + } + if (payPrice > lpTokens) + { return Unexpected(tecAMM_INVALID_TOKENS); + } return *payPrice; }; // No one owns the slot or expired slot. if (auto const acct = auctionSlot[~sfAccount]; !acct || !validOwner(*acct)) { - if (auto const payPrice = getPayPrice(minSlotPrice); !payPrice) + auto const payPrice = getPayPrice(minSlotPrice); + if (!payPrice) + { return {payPrice.error(), false}; - else - res = updateSlot(discountedFee, *payPrice, *payPrice); + } + + res = updateSlot(discountedFee, *payPrice, *payPrice); } else { @@ -308,7 +323,7 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour auctionSlot[sfAccount], toSTAmount(lpTokens.issue(), refund), ctx_.journal); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(ctx_.journal.debug()) << "AMM Bid: failed to refund."; return {res, false}; @@ -318,7 +333,7 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour res = updateSlot(discountedFee, *payPrice, burn); } - return {res, res == tesSUCCESS}; + return {res, isTesSuccess(res)}; } TER diff --git a/src/libxrpl/tx/transactors/dex/AMMClawback.cpp b/src/libxrpl/tx/transactors/dex/AMMClawback.cpp index 168583ff69..fb0999bfb0 100644 --- a/src/libxrpl/tx/transactors/dex/AMMClawback.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMClawback.cpp @@ -1,12 +1,12 @@ #include #include +#include +#include #include #include #include #include #include -#include -#include #include #include @@ -40,7 +40,7 @@ AMMClawback::preflight(PreflightContext const& ctx) auto const flags = ctx.tx.getFlags(); - if (flags & tfClawTwoAssets && asset.account != asset2.account) + if (((flags & tfClawTwoAssets) != 0u) && asset.account != asset2.account) { JLOG(ctx.j.trace()) << "AMMClawback: tfClawTwoAssets can only be enabled when two " "assets in the AMM pool are both issued by the issuer"; @@ -90,7 +90,8 @@ AMMClawback::preclaim(PreclaimContext const& ctx) // If AllowTrustLineClawback is not set or NoFreeze is set, return no // permission - if (!(issuerFlagsIn & lsfAllowTrustLineClawback) || (issuerFlagsIn & lsfNoFreeze)) + if (((issuerFlagsIn & lsfAllowTrustLineClawback) == 0u) || + ((issuerFlagsIn & lsfNoFreeze) != 0u)) return tecNO_PERMISSION; return tesSUCCESS; @@ -102,7 +103,7 @@ AMMClawback::doApply() Sandbox sb(&ctx_.view()); auto const ter = applyGuts(sb); - if (ter == tesSUCCESS) + if (isTesSuccess(ter)) sb.apply(ctx_.rawView()); return ter; @@ -128,8 +129,7 @@ AMMClawback::applyGuts(Sandbox& sb) if (sb.rules().enabled(fixAMMClawbackRounding)) { - // retrieve LP token balance inside the amendment gate to avoid - // inconsistent error behavior + // retrieve LP token balance inside the amendment gate to avoid inconsistent error behavior auto const lpTokenBalance = ammLPHolds(sb, *ammSle, holder, j_); if (lpTokenBalance == beast::zero) return tecAMM_BALANCE; @@ -138,7 +138,6 @@ AMMClawback::applyGuts(Sandbox& sb) !res) return res.error(); // LCOV_EXCL_LINE } - auto const expected = ammHolds(sb, *ammSle, asset, asset2, FreezeHandling::fhIGNORE_FREEZE, ctx_.journal); @@ -151,11 +150,14 @@ AMMClawback::applyGuts(Sandbox& sb) STAmount amountWithdraw; std::optional amount2Withdraw; + // calling a second time on purpose since `verifyAndAdjustLPTokenBalance` rounds and may adjust + // the balance auto const holdLPtokens = ammLPHolds(sb, *ammSle, holder, j_); if (holdLPtokens == beast::zero) return tecAMM_BALANCE; if (!clawAmount) + { // Because we are doing a two-asset withdrawal, // tfee is actually not used, so pass tfee as 0. std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) = @@ -172,9 +174,11 @@ AMMClawback::applyGuts(Sandbox& sb) 0, FreezeHandling::fhIGNORE_FREEZE, WithdrawAll::Yes, - mPriorBalance, + preFeeBalance_, ctx_.journal); + } else + { std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) = equalWithdrawMatchingOneAmount( sb, @@ -186,8 +190,9 @@ AMMClawback::applyGuts(Sandbox& sb) lptAMMBalance, holdLPtokens, *clawAmount); + } - if (result != tesSUCCESS) + if (!isTesSuccess(result)) return result; // LCOV_EXCL_LINE auto const res = @@ -199,8 +204,8 @@ AMMClawback::applyGuts(Sandbox& sb) << to_string(newLPTokenBalance.iou()) << " old balance: " << to_string(lptAMMBalance.iou()); - auto const ter = rippleCredit(sb, holder, issuer, amountWithdraw, true, j_); - if (ter != tesSUCCESS) + auto const ter = directSendNoFee(sb, holder, issuer, amountWithdraw, true, j_); + if (!isTesSuccess(ter)) return ter; // LCOV_EXCL_LINE // if the issuer issues both assets and sets flag tfClawTwoAssets, we @@ -211,8 +216,8 @@ AMMClawback::applyGuts(Sandbox& sb) return tecINTERNAL; // LCOV_EXCL_LINE auto const flags = ctx_.tx.getFlags(); - if (flags & tfClawTwoAssets) - return rippleCredit(sb, holder, issuer, *amount2Withdraw, true, j_); + if ((flags & tfClawTwoAssets) != 0u) + return directSendNoFee(sb, holder, issuer, *amount2Withdraw, true, j_); return tesSUCCESS; } @@ -235,6 +240,7 @@ AMMClawback::equalWithdrawMatchingOneAmount( auto const lpTokensWithdraw = toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac); if (lpTokensWithdraw > holdLPtokens) + { // if lptoken balance less than what the issuer intended to clawback, // clawback all the tokens. Because we are doing a two-asset withdrawal, // tfee is actually not used, so pass tfee as 0. @@ -251,8 +257,9 @@ AMMClawback::equalWithdrawMatchingOneAmount( 0, FreezeHandling::fhIGNORE_FREEZE, WithdrawAll::Yes, - mPriorBalance, + preFeeBalance_, ctx_.journal); + } auto const& rules = sb.rules(); if (rules.enabled(fixAMMClawbackRounding)) @@ -282,7 +289,7 @@ AMMClawback::equalWithdrawMatchingOneAmount( 0, FreezeHandling::fhIGNORE_FREEZE, WithdrawAll::No, - mPriorBalance, + preFeeBalance_, ctx_.journal); } @@ -301,7 +308,7 @@ AMMClawback::equalWithdrawMatchingOneAmount( 0, FreezeHandling::fhIGNORE_FREEZE, WithdrawAll::No, - mPriorBalance, + preFeeBalance_, ctx_.journal); } diff --git a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp index 1c5b2bae6f..6dee6dfd2a 100644 --- a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp @@ -1,13 +1,14 @@ #include #include #include +#include +#include +#include #include #include #include #include #include -#include -#include namespace xrpl { @@ -72,13 +73,13 @@ AMMCreate::preclaim(PreclaimContext const& ctx) return tecDUPLICATE; } - if (auto const ter = requireAuth(ctx.view, amount.issue(), accountID); ter != tesSUCCESS) + if (auto const ter = requireAuth(ctx.view, amount.issue(), accountID); !isTesSuccess(ter)) { JLOG(ctx.j.debug()) << "AMM Instance: account is not authorized, " << amount.issue(); return ter; } - if (auto const ter = requireAuth(ctx.view, amount2.issue(), accountID); ter != tesSUCCESS) + if (auto const ter = requireAuth(ctx.view, amount2.issue(), accountID); !isTesSuccess(ter)) { JLOG(ctx.j.debug()) << "AMM Instance: account is not authorized, " << amount2.issue(); return ter; @@ -163,14 +164,15 @@ AMMCreate::preclaim(PreclaimContext const& ctx) auto clawbackDisabled = [&](Issue const& issue) -> TER { if (isXRP(issue)) return tesSUCCESS; - if (auto const sle = ctx.view.read(keylet::account(issue.account)); !sle) + auto const sle = ctx.view.read(keylet::account(issue.account)); + if (!sle) return tecINTERNAL; // LCOV_EXCL_LINE - else if (sle->getFlags() & lsfAllowTrustLineClawback) + if (sle->getFlags() & lsfAllowTrustLineClawback) return tecNO_PERMISSION; return tesSUCCESS; }; - if (auto const ter = clawbackDisabled(amount.issue()); ter != tesSUCCESS) + if (auto const ter = clawbackDisabled(amount.issue()); !isTesSuccess(ter)) return ter; return clawbackDisabled(amount2.issue()); } @@ -231,7 +233,7 @@ applyCreate(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::J // Send LPT to LP. auto res = accountSend(sb, accountId, account_, lpTokens, ctx_.journal); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(j_.debug()) << "AMM Instance: failed to send LPT " << lpTokens; return {res, false}; @@ -244,22 +246,22 @@ applyCreate(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::J // Set AMM flag on AMM trustline if (!isXRP(amount)) { - if (SLE::pointer sleRippleState = sb.peek(keylet::line(accountId, amount.issue())); - !sleRippleState) - return tecINTERNAL; // LCOV_EXCL_LINE - else + SLE::pointer const sleRippleState = sb.peek(keylet::line(accountId, amount.issue())); + if (!sleRippleState) { - auto const flags = sleRippleState->getFlags(); - sleRippleState->setFieldU32(sfFlags, flags | lsfAMMNode); - sb.update(sleRippleState); + return tecINTERNAL; // LCOV_EXCL_LINE } + + auto const flags = sleRippleState->getFlags(); + sleRippleState->setFieldU32(sfFlags, flags | lsfAMMNode); + sb.update(sleRippleState); } return tesSUCCESS; }; // Send asset1. res = sendAndTrustSet(amount); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(j_.debug()) << "AMM Instance: failed to send " << amount; return {res, false}; @@ -267,7 +269,7 @@ applyCreate(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::J // Send asset2. res = sendAndTrustSet(amount2); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(j_.debug()) << "AMM Instance: failed to send " << amount2; return {res, false}; @@ -279,12 +281,12 @@ applyCreate(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::J Book const book{issueIn, issueOut, std::nullopt}; auto const dir = keylet::quality(keylet::book(book), uRate); if (auto const bookExisted = static_cast(sb.read(dir)); !bookExisted) - ctx_.registry.getOrderBookDB().addOrderBook(book); + ctx_.registry.get().getOrderBookDB().addOrderBook(book); }; addOrderBook(amount.issue(), amount2.issue(), getRate(amount2, amount)); addOrderBook(amount2.issue(), amount.issue(), getRate(amount, amount2)); - return {res, res == tesSUCCESS}; + return {res, isTesSuccess(res)}; } TER diff --git a/src/libxrpl/tx/transactors/dex/AMMDelete.cpp b/src/libxrpl/tx/transactors/dex/AMMDelete.cpp index da0ea4cb80..41e88e6a07 100644 --- a/src/libxrpl/tx/transactors/dex/AMMDelete.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDelete.cpp @@ -1,9 +1,9 @@ #include +#include #include #include #include #include -#include namespace xrpl { @@ -45,7 +45,7 @@ AMMDelete::doApply() auto const ter = deleteAMMAccount(sb, ctx_.tx[sfAsset].get(), ctx_.tx[sfAsset2].get(), j_); - if (ter == tesSUCCESS || ter == tecINCOMPLETE) + if (isTesSuccess(ter) || ter == tecINCOMPLETE) sb.apply(ctx_.rawView()); return ter; diff --git a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp index 4edd199134..9a2955901b 100644 --- a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp @@ -1,11 +1,12 @@ #include #include +#include +#include +#include #include #include #include #include -#include -#include namespace xrpl { @@ -17,7 +18,6 @@ AMMDeposit::checkExtraFeatures(PreflightContext const& ctx) std::uint32_t AMMDeposit::getFlagsMask(PreflightContext const& ctx) - { return tfAMMDepositMask; } @@ -44,35 +44,35 @@ AMMDeposit::preflight(PreflightContext const& ctx) JLOG(ctx.j.debug()) << "AMM Deposit: invalid flags."; return temMALFORMED; } - if (flags & tfLPToken) + if ((flags & tfLPToken) != 0u) { // if included then both amount and amount2 are deposit min if (!lpTokens || ePrice || (amount && !amount2) || (!amount && amount2) || tradingFee) return temMALFORMED; } - else if (flags & tfSingleAsset) + else if ((flags & tfSingleAsset) != 0u) { // if included then lpTokens is deposit min if (!amount || amount2 || ePrice || tradingFee) return temMALFORMED; } - else if (flags & tfTwoAsset) + else if ((flags & tfTwoAsset) != 0u) { // if included then lpTokens is deposit min if (!amount || !amount2 || ePrice || tradingFee) return temMALFORMED; } - else if (flags & tfOneAssetLPToken) + else if ((flags & tfOneAssetLPToken) != 0u) { if (!amount || !lpTokens || amount2 || ePrice || tradingFee) return temMALFORMED; } - else if (flags & tfLimitLPToken) + else if ((flags & tfLimitLPToken) != 0u) { if (!amount || !ePrice || lpTokens || amount2 || tradingFee) return temMALFORMED; } - else if (flags & tfTwoAssetIfEmpty) + else if ((flags & tfTwoAssetIfEmpty) != 0u) { if (!amount || !amount2 || ePrice || lpTokens) return temMALFORMED; @@ -156,7 +156,7 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) if (!expected) return expected.error(); // LCOV_EXCL_LINE auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected; - if (ctx.tx.getFlags() & tfTwoAssetIfEmpty) + if ((ctx.tx.getFlags() & tfTwoAssetIfEmpty) != 0u) { if (lptAMMBalance != beast::zero) return tecAMM_NOT_EMPTY; @@ -283,7 +283,7 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) }; // amount and amount2 are deposit min in case of tfLPToken - if (!(ctx.tx.getFlags() & tfLPToken)) + if ((ctx.tx.getFlags() & tfLPToken) == 0u) { if (auto const ter = checkAmount(amount, true)) return ter; @@ -357,6 +357,7 @@ AMMDeposit::applyGuts(Sandbox& sb) &lptAMMBalance = lptAMMBalance]() -> std::pair { if (subTxType & tfTwoAsset) + { return equalDepositLimit( sb, ammAccountID, @@ -367,16 +368,24 @@ AMMDeposit::applyGuts(Sandbox& sb) *amount2, lpTokensDeposit, tfee); + } if (subTxType & tfOneAssetLPToken) + { return singleDepositTokens( sb, ammAccountID, amountBalance, *amount, lptAMMBalance, *lpTokensDeposit, tfee); + } if (subTxType & tfLimitLPToken) + { return singleDepositEPrice( sb, ammAccountID, amountBalance, *amount, lptAMMBalance, *ePrice, tfee); + } if (subTxType & tfSingleAsset) + { return singleDeposit( sb, ammAccountID, amountBalance, lptAMMBalance, *amount, lpTokensDeposit, tfee); + } if (subTxType & tfLPToken) + { return equalDepositTokens( sb, ammAccountID, @@ -387,9 +396,12 @@ AMMDeposit::applyGuts(Sandbox& sb) amount, amount2, tfee); + } if (subTxType & tfTwoAssetIfEmpty) + { return equalDepositInEmptyState( sb, ammAccountID, *amount, *amount2, lptAMMBalance.issue(), tfee); + } // should not happen. // LCOV_EXCL_START JLOG(j_.error()) << "AMM Deposit: invalid options."; @@ -397,7 +409,7 @@ AMMDeposit::applyGuts(Sandbox& sb) // LCOV_EXCL_STOP }(); - if (result == tesSUCCESS) + if (isTesSuccess(result)) { XRPL_ASSERT( newLPTokenBalance > beast::zero, @@ -411,7 +423,7 @@ AMMDeposit::applyGuts(Sandbox& sb) sb.update(ammSle); } - return {result, result == tesSUCCESS}; + return {result, isTesSuccess(result)}; } TER @@ -463,7 +475,9 @@ AMMDeposit::deposit( depositAmount.issue(), FreezeHandling::fhIGNORE_FREEZE, ctx_.journal) >= depositAmount) + { return tesSUCCESS; + } return tecUNFUNDED_AMM; }; @@ -505,7 +519,7 @@ AMMDeposit::deposit( auto res = accountSend( view, account_, ammAccount, amountDepositActual, ctx_.journal, WaiveTransferFee::Yes); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit " << amountDepositActual; return {res, STAmount{}}; @@ -524,7 +538,7 @@ AMMDeposit::deposit( res = accountSend( view, account_, ammAccount, *amount2DepositActual, ctx_.journal, WaiveTransferFee::Yes); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit " << *amount2DepositActual; @@ -534,7 +548,7 @@ AMMDeposit::deposit( // Deposit LP tokens res = accountSend(view, ammAccount, account_, lpTokensDepositActual, ctx_.journal); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit LPTokens"; return {res, STAmount{}}; @@ -647,14 +661,17 @@ AMMDeposit::equalDepositLimit( if (tokensAdj == beast::zero) { if (!view.rules().enabled(fixAMMv1_3)) + { return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE - else - return {tecAMM_INVALID_TOKENS, STAmount{}}; + } + + return {tecAMM_INVALID_TOKENS, STAmount{}}; } // factor in the adjusted tokens frac = adjustFracByTokens(view.rules(), lptAMMBalance, tokensAdj, frac); auto const amount2Deposit = getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::Yes); if (amount2Deposit <= amount2) + { return deposit( view, ammAccount, @@ -667,19 +684,23 @@ AMMDeposit::equalDepositLimit( std::nullopt, lpTokensDepositMin, tfee); + } frac = Number{amount2} / amount2Balance; tokensAdj = getRoundedLPTokens(view.rules(), lptAMMBalance, frac, IsDeposit::Yes); if (tokensAdj == beast::zero) { if (!view.rules().enabled(fixAMMv1_3)) + { return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE - else - return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE + } + + return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE } // factor in the adjusted tokens frac = adjustFracByTokens(view.rules(), lptAMMBalance, tokensAdj, frac); auto const amountDeposit = getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::Yes); if (amountDeposit <= amount) + { return deposit( view, ammAccount, @@ -692,6 +713,7 @@ AMMDeposit::equalDepositLimit( std::nullopt, lpTokensDepositMin, tfee); + } return {tecAMM_FAILED, STAmount{}}; } @@ -718,9 +740,11 @@ AMMDeposit::singleDeposit( if (tokens == beast::zero) { if (!view.rules().enabled(fixAMMv1_3)) + { return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE - else - return {tecAMM_INVALID_TOKENS, STAmount{}}; + } + + return {tecAMM_INVALID_TOKENS, STAmount{}}; } // factor in the adjusted tokens auto const [tokensAdj, amountDepositAdj] = @@ -821,9 +845,11 @@ AMMDeposit::singleDepositEPrice( if (tokens <= beast::zero) { if (!view.rules().enabled(fixAMMv1_3)) + { return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE - else - return {tecAMM_INVALID_TOKENS, STAmount{}}; + } + + return {tecAMM_INVALID_TOKENS, STAmount{}}; } // factor in the adjusted tokens auto const [tokensAdj, amountDepositAdj] = @@ -832,6 +858,7 @@ AMMDeposit::singleDepositEPrice( return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE auto const ep = Number{amountDepositAdj} / tokensAdj; if (ep <= ePrice) + { return deposit( view, ammAccount, @@ -844,6 +871,7 @@ AMMDeposit::singleDepositEPrice( std::nullopt, std::nullopt, tfee); + } } // LPTokens is asset out => E = b / t diff --git a/src/libxrpl/tx/transactors/dex/AMMVote.cpp b/src/libxrpl/tx/transactors/dex/AMMVote.cpp index d784b67f88..e1b3afcd08 100644 --- a/src/libxrpl/tx/transactors/dex/AMMVote.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMVote.cpp @@ -1,8 +1,8 @@ #include +#include #include #include #include -#include #include namespace xrpl { @@ -35,15 +35,17 @@ AMMVote::preflight(PreflightContext const& ctx) TER AMMVote::preclaim(PreclaimContext const& ctx) { - if (auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2])); !ammSle) + auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2])); + if (!ammSle) { JLOG(ctx.j.debug()) << "AMM Vote: Invalid asset pair."; return terNO_AMM; } - else if (ammSle->getFieldAmount(sfLPTokenBalance) == beast::zero) + if (ammSle->getFieldAmount(sfLPTokenBalance) == beast::zero) + { return tecAMM_EMPTY; - else if ( - auto const lpTokensNew = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j); + } + if (auto const lpTokensNew = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j); lpTokensNew == beast::zero) { JLOG(ctx.j.debug()) << "AMM Vote: account is not LP."; @@ -134,16 +136,22 @@ applyVote(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jou num += feeNew * lpTokensNew; den += lpTokensNew; if (minPos) + { *(updatedVoteSlots.begin() + *minPos) = std::move(newEntry); + } else + { updatedVoteSlots.push_back(std::move(newEntry)); + } }; // Add new entry if the number of the vote entries // is less than Max. if (updatedVoteSlots.size() < VOTE_MAX_SLOTS) + { update(); - // Add the entry if the account has more tokens than - // the least token holder or same tokens and higher fee. + // Add the entry if the account has more tokens than + // the least token holder or same tokens and higher fee. + } else if (lpTokensNew > *minTokens || (lpTokensNew == *minTokens && feeNew > minFee)) { auto const entry = updatedVoteSlots.begin() + minPos; @@ -174,9 +182,13 @@ applyVote(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jou { auto& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot); if (auto const discountedFee = fee / AUCTION_SLOT_DISCOUNTED_FEE_FRACTION) + { auctionSlot.setFieldU16(sfDiscountedFee, discountedFee); + } else if (auctionSlot.isFieldPresent(sfDiscountedFee)) + { auctionSlot.makeFieldAbsent(sfDiscountedFee); + } } } else diff --git a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index cf35de321d..0c6419f842 100644 --- a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -1,9 +1,9 @@ #include #include +#include +#include #include #include -#include -#include #include namespace xrpl { @@ -42,37 +42,37 @@ AMMWithdraw::preflight(PreflightContext const& ctx) JLOG(ctx.j.debug()) << "AMM Withdraw: invalid flags."; return temMALFORMED; } - if (flags & tfLPToken) + if ((flags & tfLPToken) != 0u) { if (!lpTokens || amount || amount2 || ePrice) return temMALFORMED; } - else if (flags & tfWithdrawAll) + else if ((flags & tfWithdrawAll) != 0u) { if (lpTokens || amount || amount2 || ePrice) return temMALFORMED; } - else if (flags & tfOneAssetWithdrawAll) + else if ((flags & tfOneAssetWithdrawAll) != 0u) { if (!amount || lpTokens || amount2 || ePrice) return temMALFORMED; } - else if (flags & tfSingleAsset) + else if ((flags & tfSingleAsset) != 0u) { if (!amount || lpTokens || amount2 || ePrice) return temMALFORMED; } - else if (flags & tfTwoAsset) + else if ((flags & tfTwoAsset) != 0u) { if (!amount || !amount2 || lpTokens || ePrice) return temMALFORMED; } - else if (flags & tfOneAssetLPToken) + else if ((flags & tfOneAssetLPToken) != 0u) { if (!amount || !lpTokens || amount2 || ePrice) return temMALFORMED; } - else if (flags & tfLimitLPToken) + else if ((flags & tfLimitLPToken) != 0u) { if (!amount || !ePrice || lpTokens || amount2) return temMALFORMED; @@ -104,7 +104,7 @@ AMMWithdraw::preflight(PreflightContext const& ctx) if (auto const res = invalidAMMAmount( *amount, std::make_optional(std::make_pair(asset, asset2)), - (flags & (tfOneAssetWithdrawAll | tfOneAssetLPToken)) || ePrice)) + ((flags & (tfOneAssetWithdrawAll | tfOneAssetLPToken)) != 0u) || ePrice)) { JLOG(ctx.j.debug()) << "AMM Withdraw: invalid Asset1Out"; return res; @@ -139,7 +139,7 @@ tokensWithdraw( std::optional const& tokensIn, std::uint32_t flags) { - if (flags & (tfWithdrawAll | tfOneAssetWithdrawAll)) + if ((flags & (tfWithdrawAll | tfOneAssetWithdrawAll)) != 0u) return lpTokens; return tokensIn; } @@ -248,7 +248,7 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx) return temBAD_AMM_TOKENS; } - if (ctx.tx.getFlags() & (tfLPToken | tfWithdrawAll)) + if ((ctx.tx.getFlags() & (tfLPToken | tfWithdrawAll)) != 0u) { if (auto const ter = checkAmount(amountBalance, amountBalance)) return ter; @@ -305,6 +305,7 @@ AMMWithdraw::applyGuts(Sandbox& sb) &lptAMMBalance = lptAMMBalance]() -> std::pair { if (subTxType & tfTwoAsset) + { return equalWithdrawLimit( sb, *ammSle, @@ -315,7 +316,9 @@ AMMWithdraw::applyGuts(Sandbox& sb) *amount, *amount2, tfee); + } if (subTxType & tfOneAssetLPToken || subTxType & tfOneAssetWithdrawAll) + { return singleWithdrawTokens( sb, *ammSle, @@ -325,12 +328,17 @@ AMMWithdraw::applyGuts(Sandbox& sb) *amount, *lpTokensWithdraw, tfee); + } if (subTxType & tfLimitLPToken) + { return singleWithdrawEPrice( sb, *ammSle, ammAccountID, amountBalance, lptAMMBalance, *amount, *ePrice, tfee); + } if (subTxType & tfSingleAsset) + { return singleWithdraw( sb, *ammSle, ammAccountID, amountBalance, lptAMMBalance, *amount, tfee); + } if (subTxType & tfLPToken || subTxType & tfWithdrawAll) { return equalWithdrawTokens( @@ -351,7 +359,7 @@ AMMWithdraw::applyGuts(Sandbox& sb) // LCOV_EXCL_STOP }(); - if (result != tesSUCCESS) + if (!isTesSuccess(result)) return {result, false}; auto const res = deleteAMMAccountIfEmpty( @@ -414,7 +422,7 @@ AMMWithdraw::withdraw( tfee, FreezeHandling::fhZERO_IF_FROZEN, isWithdrawAll(ctx_.tx), - mPriorBalance, + preFeeBalance_, j_); return {ter, newLPTokenBalance}; } @@ -449,6 +457,7 @@ AMMWithdraw::withdraw( auto const [amountWithdrawActual, amount2WithdrawActual, lpTokensWithdrawActual] = [&]() -> std::tuple, STAmount> { if (withdrawAll == WithdrawAll::No) + { return adjustAmountsByLPTokens( amountBalance, amountWithdraw, @@ -457,6 +466,7 @@ AMMWithdraw::withdraw( lpTokensWithdraw, tfee, IsDeposit::No); + } return std::make_tuple(amountWithdraw, amount2Withdraw, lpTokensWithdraw); }(); @@ -528,7 +538,7 @@ AMMWithdraw::withdraw( auto const balance = (*sleAccount)[sfBalance].xrp(); std::uint32_t const ownerCount = sleAccount->at(sfOwnerCount); - // See also SetTrust::doApply() + // See also TrustSet::doApply() XRPAmount const reserve( (ownerCount < 2) ? XRPAmount(beast::zero) : view.fees().accountReserve(ownerCount + 1)); @@ -545,7 +555,7 @@ AMMWithdraw::withdraw( // Withdraw amountWithdraw auto res = accountSend( view, ammAccount, account, amountWithdrawActual, journal, WaiveTransferFee::Yes); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { // LCOV_EXCL_START JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw " << amountWithdrawActual; @@ -556,12 +566,12 @@ AMMWithdraw::withdraw( // Withdraw amount2Withdraw if (amount2WithdrawActual) { - if (auto const err = sufficientReserve(amount2WithdrawActual->issue()); err != tesSUCCESS) + if (auto const err = sufficientReserve(amount2WithdrawActual->issue()); !isTesSuccess(err)) return {err, STAmount{}, STAmount{}, STAmount{}}; res = accountSend( view, ammAccount, account, *amount2WithdrawActual, journal, WaiveTransferFee::Yes); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { // LCOV_EXCL_START JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw " << *amount2WithdrawActual; @@ -572,7 +582,7 @@ AMMWithdraw::withdraw( // Withdraw LP tokens res = redeemIOU(view, account, lpTokensWithdrawActual, lpTokensWithdrawActual.issue(), journal); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { // LCOV_EXCL_START JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw LPTokens"; @@ -628,7 +638,7 @@ AMMWithdraw::equalWithdrawTokens( tfee, FreezeHandling::fhZERO_IF_FROZEN, isWithdrawAll(ctx_.tx), - mPriorBalance, + preFeeBalance_, ctx_.journal); return {ter, newLPTokenBalance}; } @@ -647,10 +657,10 @@ AMMWithdraw::deleteAMMAccountIfEmpty( if (lpTokenBalance == beast::zero) { ter = deleteAMMAccount(sb, issue1, issue2, journal); - if (ter != tesSUCCESS && ter != tecINCOMPLETE) + if (!isTesSuccess(ter) && ter != tecINCOMPLETE) return {ter, false}; // LCOV_EXCL_LINE - else - updateBalance = (ter == tecINCOMPLETE); + + updateBalance = (ter == tecINCOMPLETE); } if (updateBalance) @@ -676,7 +686,7 @@ AMMWithdraw::equalWithdrawTokens( STAmount const& lpTokens, STAmount const& lpTokensWithdraw, std::uint16_t tfee, - FreezeHandling freezeHanding, + FreezeHandling freezeHandling, WithdrawAll withdrawAll, XRPAmount const& priorBalance, beast::Journal const& journal) @@ -697,7 +707,7 @@ AMMWithdraw::equalWithdrawTokens( lptAMMBalance, lpTokensWithdraw, tfee, - freezeHanding, + freezeHandling, WithdrawAll::Yes, priorBalance, journal); @@ -731,7 +741,7 @@ AMMWithdraw::equalWithdrawTokens( lptAMMBalance, tokensAdj, tfee, - freezeHanding, + freezeHandling, withdrawAll, priorBalance, journal); @@ -821,7 +831,9 @@ AMMWithdraw::equalWithdrawLimit( // LCOV_EXCL_STOP } else if (amountWithdraw > amount) + { return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE + } return withdraw( view, ammSle, @@ -857,9 +869,11 @@ AMMWithdraw::singleWithdraw( if (tokens == beast::zero) { if (!view.rules().enabled(fixAMMv1_3)) + { return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE - else - return {tecAMM_INVALID_TOKENS, STAmount{}}; + } + + return {tecAMM_INVALID_TOKENS, STAmount{}}; } // factor in the adjusted tokens auto const [tokensAdj, amountWithdrawAdj] = @@ -973,9 +987,11 @@ AMMWithdraw::singleWithdrawEPrice( if (tokensAdj <= beast::zero) { if (!view.rules().enabled(fixAMMv1_3)) + { return {tecAMM_FAILED, STAmount{}}; - else - return {tecAMM_INVALID_TOKENS, STAmount{}}; + } + + return {tecAMM_INVALID_TOKENS, STAmount{}}; } auto amtNoRoundCb = [&] { return tokensAdj / ePrice; }; auto amtProdCb = [&] { return tokensAdj / ePrice; }; @@ -1002,7 +1018,7 @@ AMMWithdraw::singleWithdrawEPrice( WithdrawAll AMMWithdraw::isWithdrawAll(STTx const& tx) { - if (tx[sfFlags] & (tfWithdrawAll | tfOneAssetWithdrawAll)) + if ((tx[sfFlags] & (tfWithdrawAll | tfOneAssetWithdrawAll)) != 0u) return WithdrawAll::Yes; return WithdrawAll::No; } diff --git a/src/libxrpl/tx/transactors/dex/CancelOffer.cpp b/src/libxrpl/tx/transactors/dex/OfferCancel.cpp similarity index 75% rename from src/libxrpl/tx/transactors/dex/CancelOffer.cpp rename to src/libxrpl/tx/transactors/dex/OfferCancel.cpp index 2549fd327e..f8164401b7 100644 --- a/src/libxrpl/tx/transactors/dex/CancelOffer.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCancel.cpp @@ -1,16 +1,16 @@ -#include #include +#include #include -#include +#include namespace xrpl { NotTEC -CancelOffer::preflight(PreflightContext const& ctx) +OfferCancel::preflight(PreflightContext const& ctx) { - if (!ctx.tx[sfOfferSequence]) + if (ctx.tx[sfOfferSequence] == 0u) { - JLOG(ctx.j.trace()) << "CancelOffer::preflight: missing sequence"; + JLOG(ctx.j.trace()) << "OfferCancel::preflight: missing sequence"; return temBAD_SEQUENCE; } @@ -20,7 +20,7 @@ CancelOffer::preflight(PreflightContext const& ctx) //------------------------------------------------------------------------------ TER -CancelOffer::preclaim(PreclaimContext const& ctx) +OfferCancel::preclaim(PreclaimContext const& ctx) { auto const id = ctx.tx[sfAccount]; auto const offerSequence = ctx.tx[sfOfferSequence]; @@ -42,7 +42,7 @@ CancelOffer::preclaim(PreclaimContext const& ctx) //------------------------------------------------------------------------------ TER -CancelOffer::doApply() +OfferCancel::doApply() { auto const offerSequence = ctx_.tx[sfOfferSequence]; @@ -53,7 +53,7 @@ CancelOffer::doApply() if (auto sleOffer = view().peek(keylet::offer(account_, offerSequence))) { JLOG(j_.debug()) << "Trying to cancel offer #" << offerSequence; - return offerDelete(view(), sleOffer, ctx_.registry.journal("View")); + return offerDelete(view(), sleOffer, ctx_.registry.get().getJournal("View")); } JLOG(j_.debug()) << "Offer #" << offerSequence << " can't be found."; diff --git a/src/libxrpl/tx/transactors/dex/CreateOffer.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp similarity index 88% rename from src/libxrpl/tx/transactors/dex/CreateOffer.cpp rename to src/libxrpl/tx/transactors/dex/OfferCreate.cpp index 515b72eae9..71343ca8ab 100644 --- a/src/libxrpl/tx/transactors/dex/CreateOffer.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -2,18 +2,22 @@ #include #include #include +#include +#include +#include +#include +#include #include #include #include #include #include #include -#include -#include +#include namespace xrpl { TxConsequences -CreateOffer::makeTxConsequences(PreflightContext const& ctx) +OfferCreate::makeTxConsequences(PreflightContext const& ctx) { auto calculateMaxXRPSpend = [](STTx const& tx) -> XRPAmount { auto const& amount{tx[sfTakerGets]}; @@ -24,16 +28,13 @@ CreateOffer::makeTxConsequences(PreflightContext const& ctx) } bool -CreateOffer::checkExtraFeatures(PreflightContext const& ctx) +OfferCreate::checkExtraFeatures(PreflightContext const& ctx) { - if (ctx.tx.isFieldPresent(sfDomainID) && !ctx.rules.enabled(featurePermissionedDEX)) - return false; - - return true; + return !ctx.tx.isFieldPresent(sfDomainID) || ctx.rules.enabled(featurePermissionedDEX); } std::uint32_t -CreateOffer::getFlagsMask(PreflightContext const& ctx) +OfferCreate::getFlagsMask(PreflightContext const& ctx) { // The tfOfferCreateMask is built assuming that PermissionedDEX is // enabled @@ -45,7 +46,7 @@ CreateOffer::getFlagsMask(PreflightContext const& ctx) } NotTEC -CreateOffer::preflight(PreflightContext const& ctx) +OfferCreate::preflight(PreflightContext const& ctx) { auto& tx = ctx.tx; auto& j = ctx.j; @@ -55,8 +56,8 @@ CreateOffer::preflight(PreflightContext const& ctx) if (tx.isFlag(tfHybrid) && !tx.isFieldPresent(sfDomainID)) return temINVALID_FLAG; - bool const bImmediateOrCancel(uTxFlags & tfImmediateOrCancel); - bool const bFillOrKill(uTxFlags & tfFillOrKill); + bool const bImmediateOrCancel((uTxFlags & tfImmediateOrCancel) != 0u); + bool const bFillOrKill((uTxFlags & tfFillOrKill) != 0u); if (bImmediateOrCancel && bFillOrKill) { @@ -78,8 +79,8 @@ CreateOffer::preflight(PreflightContext const& ctx) return temBAD_SEQUENCE; } - STAmount saTakerPays = tx[sfTakerPays]; - STAmount saTakerGets = tx[sfTakerGets]; + STAmount const saTakerPays = tx[sfTakerPays]; + STAmount const saTakerGets = tx[sfTakerGets]; if (!isLegalNet(saTakerPays) || !isLegalNet(saTakerGets)) return temBAD_AMOUNT; @@ -123,7 +124,7 @@ CreateOffer::preflight(PreflightContext const& ctx) } TER -CreateOffer::preclaim(PreclaimContext const& ctx) +OfferCreate::preclaim(PreclaimContext const& ctx) { auto const id = ctx.tx[sfAccount]; @@ -143,7 +144,7 @@ CreateOffer::preclaim(PreclaimContext const& ctx) std::uint32_t const uAccountSequence = sleCreator->getFieldU32(sfSequence); - auto viewJ = ctx.registry.journal("View"); + auto viewJ = ctx.registry.get().getJournal("View"); if (isGlobalFrozen(ctx.view, uPaysIssuerID) || isGlobalFrozen(ctx.view, uGetsIssuerID)) { @@ -178,7 +179,7 @@ CreateOffer::preclaim(PreclaimContext const& ctx) { auto result = checkAcceptAsset(ctx.view, ctx.flags, id, ctx.j, Issue(uPaysCurrency, uPaysIssuerID)); - if (result != tesSUCCESS) + if (!isTesSuccess(result)) return result; } @@ -194,7 +195,7 @@ CreateOffer::preclaim(PreclaimContext const& ctx) } TER -CreateOffer::checkAcceptAsset( +OfferCreate::checkAcceptAsset( ReadView const& view, ApplyFlags const flags, AccountID const id, @@ -202,7 +203,7 @@ CreateOffer::checkAcceptAsset( Issue const& issue) { // Only valid for custom currencies - XRPL_ASSERT(!isXRP(issue.currency), "xrpl::CreateOffer::checkAcceptAsset : input is not XRP"); + XRPL_ASSERT(!isXRP(issue.currency), "xrpl::OfferCreate::checkAcceptAsset : input is not XRP"); auto const issuerAccount = view.read(keylet::account(issue.account)); @@ -211,20 +212,22 @@ CreateOffer::checkAcceptAsset( JLOG(j.debug()) << "delay: can't receive IOUs from non-existent issuer: " << to_string(issue.account); - return (flags & tapRETRY) ? TER{terNO_ACCOUNT} : TER{tecNO_ISSUER}; + return ((flags & tapRETRY) != 0u) ? TER{terNO_ACCOUNT} : TER{tecNO_ISSUER}; } + // An account cannot create a trustline to itself, so no line can exist + // to be frozen. Additionally, an issuer can always accept its own + // issuance. if (issue.account == id) - // An account can always accept its own issuance. return tesSUCCESS; - if ((*issuerAccount)[sfFlags] & lsfRequireAuth) + if (((*issuerAccount)[sfFlags] & lsfRequireAuth) != 0u) { auto const trustLine = view.read(keylet::line(id, issue.account, issue.currency)); if (!trustLine) { - return (flags & tapRETRY) ? TER{terNO_LINE} : TER{tecNO_LINE}; + return ((flags & tapRETRY) != 0u) ? TER{terNO_LINE} : TER{tecNO_LINE}; } // Entries have a canonical representation, determined by a @@ -232,24 +235,17 @@ CreateOffer::checkAcceptAsset( // ordering. Determine which entry we need to access. bool const canonical_gt(id > issue.account); - bool const is_authorized((*trustLine)[sfFlags] & (canonical_gt ? lsfLowAuth : lsfHighAuth)); + bool const is_authorized( + ((*trustLine)[sfFlags] & (canonical_gt ? lsfLowAuth : lsfHighAuth)) != 0u); if (!is_authorized) { JLOG(j.debug()) << "delay: can't receive IOUs from issuer without auth."; - return (flags & tapRETRY) ? TER{terNO_AUTH} : TER{tecNO_AUTH}; + return ((flags & tapRETRY) != 0u) ? TER{terNO_AUTH} : TER{tecNO_AUTH}; } } - // An account can not create a trustline to itself, so no line can exist - // to be frozen. Additionally, an issuer can always accept its own - // issuance. - if (issue.account == id) - { - return tesSUCCESS; - } - auto const trustLine = view.read(keylet::line(id, issue.account, issue.currency)); if (!trustLine) @@ -259,7 +255,7 @@ CreateOffer::checkAcceptAsset( // There's no difference which side enacted deep freeze, accepting // tokens shouldn't be possible. - bool const deepFrozen = (*trustLine)[sfFlags] & (lsfLowDeepFreeze | lsfHighDeepFreeze); + bool const deepFrozen = ((*trustLine)[sfFlags] & (lsfLowDeepFreeze | lsfHighDeepFreeze)) != 0u; if (deepFrozen) { @@ -270,7 +266,7 @@ CreateOffer::checkAcceptAsset( } std::pair -CreateOffer::flowCross( +OfferCreate::flowCross( PaymentSandbox& psb, PaymentSandbox& psbCancel, Amounts const& takerAmount, @@ -315,7 +311,7 @@ CreateOffer::flowCross( // If we're creating a passive offer adjust the threshold so we only // cross offers that have a better quality than this one. std::uint32_t const txFlags = ctx_.tx.getFlags(); - if (txFlags & tfPassive) + if ((txFlags & tfPassive) != 0u) ++threshold; // Don't send more than our balance. @@ -336,21 +332,25 @@ CreateOffer::flowCross( // Special handling for the tfSell flag. STAmount deliver = takerAmount.out; OfferCrossing offerCrossing = OfferCrossing::yes; - if (txFlags & tfSell) + if ((txFlags & tfSell) != 0u) { offerCrossing = OfferCrossing::sell; // We are selling, so we will accept *more* than the offer // specified. Since we don't know how much they might offer, // we allow delivery of the largest possible amount. if (deliver.native()) + { deliver = STAmount{STAmount::cMaxNative}; + } else + { // We can't use the maximum possible currency here because // there might be a gateway transfer rate to account for. // Since the transfer rate cannot exceed 200%, we use 1/2 // maxValue for our limit. deliver = STAmount{ takerAmount.out.issue(), STAmount::cMaxValue / 2, STAmount::cMaxOffset}; + } } // Call the payment engine's flow() to do the actual work. @@ -360,9 +360,9 @@ CreateOffer::flowCross( account_, account_, paths, - true, // default path - !(txFlags & tfFillOrKill), // partial payment - true, // owner pays transfer fee + true, // default path + (txFlags & tfFillOrKill) == 0u, // partial payment + true, // owner pays transfer fee offerCrossing, threshold, sendMax, @@ -396,7 +396,7 @@ CreateOffer::flowCross( { STAmount const rate{Quality{takerAmount.out, takerAmount.in}.rate()}; - if (txFlags & tfSell) + if ((txFlags & tfSell) != 0u) { // If selling then scale the new out amount based on how // much we sold during crossing. This preserves the offer @@ -408,17 +408,21 @@ CreateOffer::flowCross( // gateway's transfer rate. STAmount nonGatewayAmountIn = result.actualAmountIn; if (gatewayXferRate.value != QUALITY_ONE) + { nonGatewayAmountIn = divideRound( result.actualAmountIn, gatewayXferRate, takerAmount.in.issue(), true); + } afterCross.in -= nonGatewayAmountIn; // It's possible that the divRound will cause our subtract // to go slightly negative. So limit afterCross.in to zero. if (afterCross.in < beast::zero) + { // We should verify that the difference *is* small, but // what is a good threshold to check? afterCross.in.clear(); + } afterCross.out = divRoundStrict(afterCross.in, rate, takerAmount.out.issue(), false); @@ -431,7 +435,7 @@ CreateOffer::flowCross( afterCross.out -= result.actualAmountOut; XRPL_ASSERT( afterCross.out >= beast::zero, - "xrpl::CreateOffer::flowCross : minimum offer"); + "xrpl::OfferCreate::flowCross : minimum offer"); if (afterCross.out < beast::zero) afterCross.out.clear(); afterCross.in = mulRound(afterCross.out, rate, takerAmount.in.issue(), true); @@ -450,7 +454,7 @@ CreateOffer::flowCross( } std::string -CreateOffer::format_amount(STAmount const& amount) +OfferCreate::format_amount(STAmount const& amount) { std::string txt = amount.getText(); txt += "/"; @@ -459,7 +463,7 @@ CreateOffer::format_amount(STAmount const& amount) } TER -CreateOffer::applyHybrid( +OfferCreate::applyHybrid( Sandbox& sb, std::shared_ptr sleOffer, Keylet const& offerKey, @@ -498,24 +502,24 @@ CreateOffer::applyHybrid( bookArr.push_back(std::move(bookInfo)); if (!bookExists) - ctx_.registry.getOrderBookDB().addOrderBook(book); + ctx_.registry.get().getOrderBookDB().addOrderBook(book); sleOffer->setFieldArray(sfAdditionalBooks, bookArr); return tesSUCCESS; } std::pair -CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) +OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel) { using beast::zero; std::uint32_t const uTxFlags = ctx_.tx.getFlags(); - bool const bPassive(uTxFlags & tfPassive); - bool const bImmediateOrCancel(uTxFlags & tfImmediateOrCancel); - bool const bFillOrKill(uTxFlags & tfFillOrKill); - bool const bSell(uTxFlags & tfSell); - bool const bHybrid(uTxFlags & tfHybrid); + bool const bPassive((uTxFlags & tfPassive) != 0u); + bool const bImmediateOrCancel((uTxFlags & tfImmediateOrCancel) != 0u); + bool const bFillOrKill((uTxFlags & tfFillOrKill) != 0u); + bool const bSell((uTxFlags & tfSell) != 0u); + bool const bHybrid((uTxFlags & tfHybrid) != 0u); auto saTakerPays = ctx_.tx[sfTakerPays]; auto saTakerGets = ctx_.tx[sfTakerGets]; @@ -532,7 +536,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) // end up on the books. auto uRate = getRate(saTakerGets, saTakerPays); - auto viewJ = ctx_.registry.journal("View"); + auto viewJ = ctx_.registry.get().getJournal("View"); TER result = tesSUCCESS; @@ -563,7 +567,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) bool const bOpenLedger = sb.open(); bool crossed = false; - if (result == tesSUCCESS) + if (isTesSuccess(result)) { // If a tick size applies, round the offer to the tick size auto const& uPaysIssuerID = saTakerPays.getIssuer(); @@ -635,8 +639,8 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) // We expect the implementation of cross to succeed // or give a tec. XRPL_ASSERT( - result == tesSUCCESS || isTecClaim(result), - "xrpl::CreateOffer::applyGuts : result is tesSUCCESS or " + isTesSuccess(result) || isTecClaim(result), + "xrpl::OfferCreate::applyGuts : result is tesSUCCESS or " "tecCLAIM"); if (auto stream = j_.trace()) @@ -649,7 +653,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) if (result == tecFAILED_PROCESSING && bOpenLedger) result = telFAILED_PROCESSING; - if (result != tesSUCCESS) + if (!isTesSuccess(result)) { JLOG(j_.debug()) << "final result: " << transToken(result); return {result, true}; @@ -657,10 +661,10 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) XRPL_ASSERT( saTakerGets.issue() == place_offer.in.issue(), - "xrpl::CreateOffer::applyGuts : taker gets issue match"); + "xrpl::OfferCreate::applyGuts : taker gets issue match"); XRPL_ASSERT( saTakerPays.issue() == place_offer.out.issue(), - "xrpl::CreateOffer::applyGuts : taker pays issue match"); + "xrpl::OfferCreate::applyGuts : taker pays issue match"); if (takerAmount != place_offer) crossed = true; @@ -690,9 +694,9 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) XRPL_ASSERT( saTakerPays > zero && saTakerGets > zero, - "xrpl::CreateOffer::applyGuts : taker pays and gets positive"); + "xrpl::OfferCreate::applyGuts : taker pays and gets positive"); - if (result != tesSUCCESS) + if (!isTesSuccess(result)) { JLOG(j_.debug()) << "final result: " << transToken(result); return {result, true}; @@ -719,10 +723,12 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) { JLOG(j_.trace()) << "Immediate or cancel: offer canceled"; if (!crossed) + { // Any ImmediateOrCancel offer that transfers absolutely no funds // returns tecKILLED rather than tesSUCCESS. Motivation for the - // change is here: https://github.com/ripple/rippled/issues/4115 + // change is here: https://github.com/XRPLF/rippled/issues/4115 return {tecKILLED, false}; + } return {tesSUCCESS, true}; } @@ -731,9 +737,10 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) return {tefINTERNAL, false}; { - XRPAmount reserve = sb.fees().accountReserve(sleCreator->getFieldU32(sfOwnerCount) + 1); + XRPAmount const reserve = + sb.fees().accountReserve(sleCreator->getFieldU32(sfOwnerCount) + 1); - if (mPriorBalance < reserve) + if (preFeeBalance_ < reserve) { // If we are here, the signing account had an insufficient reserve // *prior* to our processing. If something actually crossed, then @@ -741,7 +748,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) if (!crossed) result = tecINSUF_RESERVE_OFFER; - if (result != tesSUCCESS) + if (!isTesSuccess(result)) { JLOG(j_.debug()) << "final result: " << transToken(result); } @@ -833,14 +840,14 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) { auto const res = applyHybrid(sb, sleOffer, offer_index, saTakerPays, saTakerGets, setBookDir); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return {res, true}; // LCOV_EXCL_LINE } sb.insert(sleOffer); if (!bookExisted) - ctx_.registry.getOrderBookDB().addOrderBook(book); + ctx_.registry.get().getOrderBookDB().addOrderBook(book); JLOG(j_.debug()) << "final result: success"; @@ -848,7 +855,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) } TER -CreateOffer::doApply() +OfferCreate::doApply() { // This is the ledger view that we work against. Transactions are applied // as we go on processing transactions. @@ -861,9 +868,13 @@ CreateOffer::doApply() auto const result = applyGuts(sb, sbCancel); if (result.second) + { sb.apply(ctx_.rawView()); + } else + { sbCancel.apply(ctx_.rawView()); + } return result.first; } diff --git a/src/libxrpl/tx/transactors/did/DIDDelete.cpp b/src/libxrpl/tx/transactors/did/DIDDelete.cpp new file mode 100644 index 0000000000..0d5b63635f --- /dev/null +++ b/src/libxrpl/tx/transactors/did/DIDDelete.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include + +namespace xrpl { + +NotTEC +DIDDelete::preflight(PreflightContext const& ctx) +{ + return tesSUCCESS; +} + +TER +DIDDelete::deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner) +{ + auto const sle = ctx.view().peek(sleKeylet); + if (!sle) + return tecNO_ENTRY; + + return DIDDelete::deleteSLE(ctx.view(), sle, owner, ctx.journal); +} + +TER +DIDDelete::deleteSLE( + ApplyView& view, + std::shared_ptr sle, + AccountID const owner, + beast::Journal j) +{ + // Remove object from owner directory + if (!view.dirRemove(keylet::ownerDir(owner), (*sle)[sfOwnerNode], sle->key(), true)) + { + // LCOV_EXCL_START + JLOG(j.fatal()) << "Unable to delete DID from owner."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + + auto const sleOwner = view.peek(keylet::account(owner)); + if (!sleOwner) + return tecINTERNAL; // LCOV_EXCL_LINE + + adjustOwnerCount(view, sleOwner, -1, j); + view.update(sleOwner); + + // Remove object from ledger + view.erase(sle); + return tesSUCCESS; +} + +TER +DIDDelete::doApply() +{ + return deleteSLE(ctx_, keylet::did(account_), account_); +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/did/DID.cpp b/src/libxrpl/tx/transactors/did/DIDSet.cpp similarity index 76% rename from src/libxrpl/tx/transactors/did/DID.cpp rename to src/libxrpl/tx/transactors/did/DIDSet.cpp index 080a031c79..cd5c9bbc96 100644 --- a/src/libxrpl/tx/transactors/did/DID.cpp +++ b/src/libxrpl/tx/transactors/did/DIDSet.cpp @@ -1,10 +1,11 @@ #include #include -#include +#include +#include #include #include #include -#include +#include namespace xrpl { @@ -41,13 +42,13 @@ DIDSet::preflight(PreflightContext const& ctx) }; if (isTooLong(sfURI, maxDIDURILength) || isTooLong(sfDIDDocument, maxDIDDocumentLength) || - isTooLong(sfData, maxDIDAttestationLength)) + isTooLong(sfData, maxDIDDataLength)) return temMALFORMED; return tesSUCCESS; } -TER +static TER addSLE(ApplyContext& ctx, std::shared_ptr const& sle, AccountID const& owner) { auto const sleAccount = ctx.view().peek(keylet::account(owner)); @@ -134,54 +135,4 @@ DIDSet::doApply() return addSLE(ctx_, sleDID, account_); } -NotTEC -DIDDelete::preflight(PreflightContext const& ctx) -{ - return tesSUCCESS; -} - -TER -DIDDelete::deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner) -{ - auto const sle = ctx.view().peek(sleKeylet); - if (!sle) - return tecNO_ENTRY; - - return DIDDelete::deleteSLE(ctx.view(), sle, owner, ctx.journal); -} - -TER -DIDDelete::deleteSLE( - ApplyView& view, - std::shared_ptr sle, - AccountID const owner, - beast::Journal j) -{ - // Remove object from owner directory - if (!view.dirRemove(keylet::ownerDir(owner), (*sle)[sfOwnerNode], sle->key(), true)) - { - // LCOV_EXCL_START - JLOG(j.fatal()) << "Unable to delete DID Token from owner."; - return tefBAD_LEDGER; - // LCOV_EXCL_STOP - } - - auto const sleOwner = view.peek(keylet::account(owner)); - if (!sleOwner) - return tecINTERNAL; // LCOV_EXCL_LINE - - adjustOwnerCount(view, sleOwner, -1, j); - view.update(sleOwner); - - // Remove object from ledger - view.erase(sle); - return tesSUCCESS; -} - -TER -DIDDelete::doApply() -{ - return deleteSLE(ctx_, keylet::did(account_), account_); -} - } // namespace xrpl diff --git a/src/libxrpl/tx/transactors/escrow/Escrow.cpp b/src/libxrpl/tx/transactors/escrow/Escrow.cpp deleted file mode 100644 index 3d7d408af7..0000000000 --- a/src/libxrpl/tx/transactors/escrow/Escrow.cpp +++ /dev/null @@ -1,1262 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace xrpl { - -// During an EscrowFinish, the transaction must specify both -// a condition and a fulfillment. We track whether that -// fulfillment matches and validates the condition. -constexpr HashRouterFlags SF_CF_INVALID = HashRouterFlags::PRIVATE5; -constexpr HashRouterFlags SF_CF_VALID = HashRouterFlags::PRIVATE6; - -/* - Escrow - ====== - - Escrow is a feature of the XRP Ledger that allows you to send conditional - XRP payments. These conditional payments, called escrows, set aside XRP and - deliver it later when certain conditions are met. Conditions to successfully - finish an escrow include time-based unlocks and crypto-conditions. Escrows - can also be set to expire if not finished in time. - - The XRP set aside in an escrow is locked up. No one can use or destroy the - XRP until the escrow has been successfully finished or canceled. Before the - expiration time, only the intended receiver can get the XRP. After the - expiration time, the XRP can only be returned to the sender. - - For more details on escrow, including examples, diagrams and more please - visit https://xrpl.org/escrow.html - - For details on specific transactions, including fields and validation rules - please see: - - `EscrowCreate` - -------------- - See: https://xrpl.org/escrowcreate.html - - `EscrowFinish` - -------------- - See: https://xrpl.org/escrowfinish.html - - `EscrowCancel` - -------------- - See: https://xrpl.org/escrowcancel.html -*/ - -//------------------------------------------------------------------------------ - -TxConsequences -EscrowCreate::makeTxConsequences(PreflightContext const& ctx) -{ - auto const amount = ctx.tx[sfAmount]; - return TxConsequences{ctx.tx, isXRP(amount) ? amount.xrp() : beast::zero}; -} - -template -static NotTEC -escrowCreatePreflightHelper(PreflightContext const& ctx); - -template <> -NotTEC -escrowCreatePreflightHelper(PreflightContext const& ctx) -{ - STAmount const amount = ctx.tx[sfAmount]; - if (amount.native() || amount <= beast::zero) - return temBAD_AMOUNT; - - if (badCurrency() == amount.getCurrency()) - return temBAD_CURRENCY; - - return tesSUCCESS; -} - -template <> -NotTEC -escrowCreatePreflightHelper(PreflightContext const& ctx) -{ - if (!ctx.rules.enabled(featureMPTokensV1)) - return temDISABLED; - - auto const amount = ctx.tx[sfAmount]; - if (amount.native() || amount.mpt() > MPTAmount{maxMPTokenAmount} || amount <= beast::zero) - return temBAD_AMOUNT; - - return tesSUCCESS; -} - -NotTEC -EscrowCreate::preflight(PreflightContext const& ctx) -{ - STAmount const amount{ctx.tx[sfAmount]}; - if (!isXRP(amount)) - { - if (!ctx.rules.enabled(featureTokenEscrow)) - return temBAD_AMOUNT; - - if (auto const ret = std::visit( - [&](T const&) { return escrowCreatePreflightHelper(ctx); }, - amount.asset().value()); - !isTesSuccess(ret)) - return ret; - } - else - { - if (amount <= beast::zero) - return temBAD_AMOUNT; - } - - // We must specify at least one timeout value - if (!ctx.tx[~sfCancelAfter] && !ctx.tx[~sfFinishAfter]) - return temBAD_EXPIRATION; - - // If both finish and cancel times are specified then the cancel time must - // be strictly after the finish time. - if (ctx.tx[~sfCancelAfter] && ctx.tx[~sfFinishAfter] && - ctx.tx[sfCancelAfter] <= ctx.tx[sfFinishAfter]) - return temBAD_EXPIRATION; - - // In the absence of a FinishAfter, the escrow can be finished - // immediately, which can be confusing. When creating an escrow, - // we want to ensure that either a FinishAfter time is explicitly - // specified or a completion condition is attached. - if (!ctx.tx[~sfFinishAfter] && !ctx.tx[~sfCondition]) - return temMALFORMED; - - if (auto const cb = ctx.tx[~sfCondition]) - { - using namespace xrpl::cryptoconditions; - - std::error_code ec; - - auto condition = Condition::deserialize(*cb, ec); - if (!condition) - { - JLOG(ctx.j.debug()) << "Malformed condition during escrow creation: " << ec.message(); - return temMALFORMED; - } - } - - return tesSUCCESS; -} - -template -static TER -escrowCreatePreclaimHelper( - PreclaimContext const& ctx, - AccountID const& account, - AccountID const& dest, - STAmount const& amount); - -template <> -TER -escrowCreatePreclaimHelper( - PreclaimContext const& ctx, - AccountID const& account, - AccountID const& dest, - STAmount const& amount) -{ - AccountID issuer = amount.getIssuer(); - // If the issuer is the same as the account, return tecNO_PERMISSION - if (issuer == account) - return tecNO_PERMISSION; - - // If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION - auto const sleIssuer = ctx.view.read(keylet::account(issuer)); - if (!sleIssuer) - return tecNO_ISSUER; - if (!sleIssuer->isFlag(lsfAllowTrustLineLocking)) - return tecNO_PERMISSION; - - // If the account does not have a trustline to the issuer, return tecNO_LINE - auto const sleRippleState = ctx.view.read(keylet::line(account, issuer, amount.getCurrency())); - if (!sleRippleState) - return tecNO_LINE; - - STAmount const balance = (*sleRippleState)[sfBalance]; - - // If balance is positive, issuer must have higher address than account - if (balance > beast::zero && issuer < account) - return tecNO_PERMISSION; // LCOV_EXCL_LINE - - // If balance is negative, issuer must have lower address than account - if (balance < beast::zero && issuer > account) - return tecNO_PERMISSION; // LCOV_EXCL_LINE - - // If the issuer has requireAuth set, check if the account is authorized - if (auto const ter = requireAuth(ctx.view, amount.issue(), account); ter != tesSUCCESS) - return ter; - - // If the issuer has requireAuth set, check if the destination is authorized - if (auto const ter = requireAuth(ctx.view, amount.issue(), dest); ter != tesSUCCESS) - return ter; - - // If the issuer has frozen the account, return tecFROZEN - if (isFrozen(ctx.view, account, amount.issue())) - return tecFROZEN; - - // If the issuer has frozen the destination, return tecFROZEN - if (isFrozen(ctx.view, dest, amount.issue())) - return tecFROZEN; - - STAmount const spendableAmount = - accountHolds(ctx.view, account, amount.getCurrency(), issuer, fhIGNORE_FREEZE, ctx.j); - - // If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS - if (spendableAmount <= beast::zero) - return tecINSUFFICIENT_FUNDS; - - // If the spendable amount is less than the amount, return - // tecINSUFFICIENT_FUNDS - if (spendableAmount < amount) - return tecINSUFFICIENT_FUNDS; - - // If the amount is not addable to the balance, return tecPRECISION_LOSS - if (!canAdd(spendableAmount, amount)) - return tecPRECISION_LOSS; - - return tesSUCCESS; -} - -template <> -TER -escrowCreatePreclaimHelper( - PreclaimContext const& ctx, - AccountID const& account, - AccountID const& dest, - STAmount const& amount) -{ - AccountID issuer = amount.getIssuer(); - // If the issuer is the same as the account, return tecNO_PERMISSION - if (issuer == account) - return tecNO_PERMISSION; - - // If the mpt does not exist, return tecOBJECT_NOT_FOUND - auto const issuanceKey = keylet::mptIssuance(amount.get().getMptID()); - auto const sleIssuance = ctx.view.read(issuanceKey); - if (!sleIssuance) - return tecOBJECT_NOT_FOUND; - - // If the lsfMPTCanEscrow is not enabled, return tecNO_PERMISSION - if (!sleIssuance->isFlag(lsfMPTCanEscrow)) - return tecNO_PERMISSION; - - // If the issuer is not the same as the issuer of the mpt, return - // tecNO_PERMISSION - if (sleIssuance->getAccountID(sfIssuer) != issuer) - return tecNO_PERMISSION; // LCOV_EXCL_LINE - - // If the account does not have the mpt, return tecOBJECT_NOT_FOUND - if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, account))) - return tecOBJECT_NOT_FOUND; - - // If the issuer has requireAuth set, check if the account is - // authorized - auto const& mptIssue = amount.get(); - if (auto const ter = requireAuth(ctx.view, mptIssue, account, AuthType::WeakAuth); - ter != tesSUCCESS) - return ter; - - // If the issuer has requireAuth set, check if the destination is - // authorized - if (auto const ter = requireAuth(ctx.view, mptIssue, dest, AuthType::WeakAuth); - ter != tesSUCCESS) - return ter; - - // If the issuer has frozen the account, return tecLOCKED - if (isFrozen(ctx.view, account, mptIssue)) - return tecLOCKED; - - // If the issuer has frozen the destination, return tecLOCKED - if (isFrozen(ctx.view, dest, mptIssue)) - return tecLOCKED; - - // If the mpt cannot be transferred, return tecNO_AUTH - if (auto const ter = canTransfer(ctx.view, mptIssue, account, dest); ter != tesSUCCESS) - return ter; - - STAmount const spendableAmount = accountHolds( - ctx.view, account, amount.get(), fhIGNORE_FREEZE, ahIGNORE_AUTH, ctx.j); - - // If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS - if (spendableAmount <= beast::zero) - return tecINSUFFICIENT_FUNDS; - - // If the spendable amount is less than the amount, return - // tecINSUFFICIENT_FUNDS - if (spendableAmount < amount) - return tecINSUFFICIENT_FUNDS; - - return tesSUCCESS; -} - -TER -EscrowCreate::preclaim(PreclaimContext const& ctx) -{ - STAmount const amount{ctx.tx[sfAmount]}; - AccountID const account{ctx.tx[sfAccount]}; - AccountID const dest{ctx.tx[sfDestination]}; - - auto const sled = ctx.view.read(keylet::account(dest)); - if (!sled) - return tecNO_DST; - - // Pseudo-accounts cannot receive escrow. Note, this is not amendment-gated - // because all writes to pseudo-account discriminator fields **are** - // amendment gated, hence the behaviour of this check will always match the - // currently active amendments. - if (isPseudoAccount(sled)) - return tecNO_PERMISSION; - - if (!isXRP(amount)) - { - if (!ctx.view.rules().enabled(featureTokenEscrow)) - return temDISABLED; // LCOV_EXCL_LINE - - if (auto const ret = std::visit( - [&](T const&) { - return escrowCreatePreclaimHelper(ctx, account, dest, amount); - }, - amount.asset().value()); - !isTesSuccess(ret)) - return ret; - } - return tesSUCCESS; -} - -template -static TER -escrowLockApplyHelper( - ApplyView& view, - AccountID const& issuer, - AccountID const& sender, - STAmount const& amount, - beast::Journal journal); - -template <> -TER -escrowLockApplyHelper( - ApplyView& view, - AccountID const& issuer, - AccountID const& sender, - STAmount const& amount, - beast::Journal journal) -{ - // Defensive: Issuer cannot create an escrow - if (issuer == sender) - return tecINTERNAL; // LCOV_EXCL_LINE - - auto const ter = rippleCredit( - view, sender, issuer, amount, amount.holds() ? false : true, journal); - if (ter != tesSUCCESS) - return ter; // LCOV_EXCL_LINE - return tesSUCCESS; -} - -template <> -TER -escrowLockApplyHelper( - ApplyView& view, - AccountID const& issuer, - AccountID const& sender, - STAmount const& amount, - beast::Journal journal) -{ - // Defensive: Issuer cannot create an escrow - if (issuer == sender) - return tecINTERNAL; // LCOV_EXCL_LINE - - auto const ter = rippleLockEscrowMPT(view, sender, amount, journal); - if (ter != tesSUCCESS) - return ter; // LCOV_EXCL_LINE - return tesSUCCESS; -} - -TER -EscrowCreate::doApply() -{ - auto const closeTime = ctx_.view().header().parentCloseTime; - - if (ctx_.tx[~sfCancelAfter] && after(closeTime, ctx_.tx[sfCancelAfter])) - return tecNO_PERMISSION; - - if (ctx_.tx[~sfFinishAfter] && after(closeTime, ctx_.tx[sfFinishAfter])) - return tecNO_PERMISSION; - - auto const sle = ctx_.view().peek(keylet::account(account_)); - if (!sle) - return tefINTERNAL; // LCOV_EXCL_LINE - - // Check reserve and funds availability - STAmount const amount{ctx_.tx[sfAmount]}; - - auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1); - - if (mSourceBalance < reserve) - return tecINSUFFICIENT_RESERVE; - - // Check reserve and funds availability - if (isXRP(amount)) - { - if (mSourceBalance < reserve + STAmount(amount).xrp()) - return tecUNFUNDED; - } - - // Check destination account - { - auto const sled = ctx_.view().read(keylet::account(ctx_.tx[sfDestination])); - if (!sled) - return tecNO_DST; // LCOV_EXCL_LINE - if (((*sled)[sfFlags] & lsfRequireDestTag) && !ctx_.tx[~sfDestinationTag]) - return tecDST_TAG_NEEDED; - } - - // Create escrow in ledger. Note that we we use the value from the - // sequence or ticket. For more explanation see comments in SeqProxy.h. - Keylet const escrowKeylet = keylet::escrow(account_, ctx_.tx.getSeqValue()); - auto const slep = std::make_shared(escrowKeylet); - (*slep)[sfAmount] = amount; - (*slep)[sfAccount] = account_; - (*slep)[~sfCondition] = ctx_.tx[~sfCondition]; - (*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag]; - (*slep)[sfDestination] = ctx_.tx[sfDestination]; - (*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter]; - (*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter]; - (*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag]; - - if (ctx_.view().rules().enabled(fixIncludeKeyletFields)) - { - (*slep)[sfSequence] = ctx_.tx.getSeqValue(); - } - - if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount)) - { - auto const xferRate = transferRate(ctx_.view(), amount); - if (xferRate != parityRate) - (*slep)[sfTransferRate] = xferRate.value; - } - - ctx_.view().insert(slep); - - // Add escrow to sender's owner directory - { - auto page = ctx_.view().dirInsert( - keylet::ownerDir(account_), escrowKeylet, describeOwnerDir(account_)); - if (!page) - return tecDIR_FULL; // LCOV_EXCL_LINE - (*slep)[sfOwnerNode] = *page; - } - - // If it's not a self-send, add escrow to recipient's owner directory. - AccountID const dest = ctx_.tx[sfDestination]; - if (dest != account_) - { - auto page = - ctx_.view().dirInsert(keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest)); - if (!page) - return tecDIR_FULL; // LCOV_EXCL_LINE - (*slep)[sfDestinationNode] = *page; - } - - // IOU escrow objects are added to the issuer's owner directory to help - // track the total locked balance. For MPT, this isn't necessary because the - // locked balance is already stored directly in the MPTokenIssuance object. - AccountID const issuer = amount.getIssuer(); - if (!isXRP(amount) && issuer != account_ && issuer != dest && !amount.holds()) - { - auto page = - ctx_.view().dirInsert(keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer)); - if (!page) - return tecDIR_FULL; // LCOV_EXCL_LINE - (*slep)[sfIssuerNode] = *page; - } - - // Deduct owner's balance - if (isXRP(amount)) - (*sle)[sfBalance] = (*sle)[sfBalance] - amount; - else - { - if (auto const ret = std::visit( - [&](T const&) { - return escrowLockApplyHelper(ctx_.view(), issuer, account_, amount, j_); - }, - amount.asset().value()); - !isTesSuccess(ret)) - { - return ret; // LCOV_EXCL_LINE - } - } - - // increment owner count - adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); - ctx_.view().update(sle); - return tesSUCCESS; -} - -//------------------------------------------------------------------------------ - -static bool -checkCondition(Slice f, Slice c) -{ - using namespace xrpl::cryptoconditions; - - std::error_code ec; - - auto condition = Condition::deserialize(c, ec); - if (!condition) - return false; - - auto fulfillment = Fulfillment::deserialize(f, ec); - if (!fulfillment) - return false; - - return validate(*fulfillment, *condition); -} - -bool -EscrowFinish::checkExtraFeatures(PreflightContext const& ctx) -{ - return !ctx.tx.isFieldPresent(sfCredentialIDs) || ctx.rules.enabled(featureCredentials); -} - -NotTEC -EscrowFinish::preflight(PreflightContext const& ctx) -{ - auto const cb = ctx.tx[~sfCondition]; - auto const fb = ctx.tx[~sfFulfillment]; - - // If you specify a condition, then you must also specify - // a fulfillment. - if (static_cast(cb) != static_cast(fb)) - return temMALFORMED; - - return tesSUCCESS; -} - -NotTEC -EscrowFinish::preflightSigValidated(PreflightContext const& ctx) -{ - auto const cb = ctx.tx[~sfCondition]; - auto const fb = ctx.tx[~sfFulfillment]; - - if (cb && fb) - { - auto& router = ctx.registry.getHashRouter(); - - auto const id = ctx.tx.getTransactionID(); - auto const flags = router.getFlags(id); - - // If we haven't checked the condition, check it - // now. Whether it passes or not isn't important - // in preflight. - if (!any(flags & (SF_CF_INVALID | SF_CF_VALID))) - { - if (checkCondition(*fb, *cb)) - router.setFlags(id, SF_CF_VALID); - else - router.setFlags(id, SF_CF_INVALID); - } - } - - if (auto const err = credentials::checkFields(ctx.tx, ctx.j); !isTesSuccess(err)) - return err; - - return tesSUCCESS; -} - -XRPAmount -EscrowFinish::calculateBaseFee(ReadView const& view, STTx const& tx) -{ - XRPAmount extraFee{0}; - - if (auto const fb = tx[~sfFulfillment]) - { - extraFee += view.fees().base * (32 + (fb->size() / 16)); - } - - return Transactor::calculateBaseFee(view, tx) + extraFee; -} - -template -static TER -escrowFinishPreclaimHelper( - PreclaimContext const& ctx, - AccountID const& dest, - STAmount const& amount); - -template <> -TER -escrowFinishPreclaimHelper( - PreclaimContext const& ctx, - AccountID const& dest, - STAmount const& amount) -{ - AccountID issuer = amount.getIssuer(); - // If the issuer is the same as the account, return tesSUCCESS - if (issuer == dest) - return tesSUCCESS; - - // If the issuer has requireAuth set, check if the destination is authorized - if (auto const ter = requireAuth(ctx.view, amount.issue(), dest); ter != tesSUCCESS) - return ter; - - // If the issuer has deep frozen the destination, return tecFROZEN - if (isDeepFrozen(ctx.view, dest, amount.getCurrency(), amount.getIssuer())) - return tecFROZEN; - - return tesSUCCESS; -} - -template <> -TER -escrowFinishPreclaimHelper( - PreclaimContext const& ctx, - AccountID const& dest, - STAmount const& amount) -{ - AccountID issuer = amount.getIssuer(); - // If the issuer is the same as the dest, return tesSUCCESS - if (issuer == dest) - return tesSUCCESS; - - // If the mpt does not exist, return tecOBJECT_NOT_FOUND - auto const issuanceKey = keylet::mptIssuance(amount.get().getMptID()); - auto const sleIssuance = ctx.view.read(issuanceKey); - if (!sleIssuance) - return tecOBJECT_NOT_FOUND; - - // If the issuer has requireAuth set, check if the destination is - // authorized - auto const& mptIssue = amount.get(); - if (auto const ter = requireAuth(ctx.view, mptIssue, dest, AuthType::WeakAuth); - ter != tesSUCCESS) - return ter; - - // If the issuer has frozen the destination, return tecLOCKED - if (isFrozen(ctx.view, dest, mptIssue)) - return tecLOCKED; - - return tesSUCCESS; -} - -TER -EscrowFinish::preclaim(PreclaimContext const& ctx) -{ - if (ctx.view.rules().enabled(featureCredentials)) - { - if (auto const err = credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j); - !isTesSuccess(err)) - return err; - } - - if (ctx.view.rules().enabled(featureTokenEscrow)) - { - auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]); - auto const slep = ctx.view.read(k); - if (!slep) - return tecNO_TARGET; - - AccountID const dest = (*slep)[sfDestination]; - STAmount const amount = (*slep)[sfAmount]; - - if (!isXRP(amount)) - { - if (auto const ret = std::visit( - [&](T const&) { - return escrowFinishPreclaimHelper(ctx, dest, amount); - }, - amount.asset().value()); - !isTesSuccess(ret)) - return ret; - } - } - return tesSUCCESS; -} - -template -static TER -escrowUnlockApplyHelper( - ApplyView& view, - Rate lockedRate, - std::shared_ptr const& sleDest, - STAmount const& xrpBalance, - STAmount const& amount, - AccountID const& issuer, - AccountID const& sender, - AccountID const& receiver, - bool createAsset, - beast::Journal journal); - -template <> -TER -escrowUnlockApplyHelper( - ApplyView& view, - Rate lockedRate, - std::shared_ptr const& sleDest, - STAmount const& xrpBalance, - STAmount const& amount, - AccountID const& issuer, - AccountID const& sender, - AccountID const& receiver, - bool createAsset, - beast::Journal journal) -{ - Keylet const trustLineKey = keylet::line(receiver, amount.issue()); - bool const recvLow = issuer > receiver; - bool const senderIssuer = issuer == sender; - bool const receiverIssuer = issuer == receiver; - bool const issuerHigh = issuer > receiver; - - if (senderIssuer) - return tecINTERNAL; // LCOV_EXCL_LINE - - if (receiverIssuer) - return tesSUCCESS; - - if (!view.exists(trustLineKey) && createAsset && !receiverIssuer) - { - // Can the account cover the trust line's reserve? - if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)}; - xrpBalance < view.fees().accountReserve(ownerCount + 1)) - { - JLOG(journal.trace()) << "Trust line does not exist. " - "Insufficent reserve to create line."; - - return tecNO_LINE_INSUF_RESERVE; - } - - Currency const currency = amount.getCurrency(); - STAmount initialBalance(amount.issue()); - initialBalance.setIssuer(noAccount()); - - // clang-format off - if (TER const ter = trustCreate( - view, // payment sandbox - recvLow, // is dest low? - issuer, // source - receiver, // destination - trustLineKey.key, // ledger index - sleDest, // Account to add to - false, // authorize account - (sleDest->getFlags() & lsfDefaultRipple) == 0, - false, // freeze trust line - false, // deep freeze trust line - initialBalance, // zero initial balance - Issue(currency, receiver), // limit of zero - 0, // quality in - 0, // quality out - journal); // journal - !isTesSuccess(ter)) - { - return ter; // LCOV_EXCL_LINE - } - // clang-format on - - view.update(sleDest); - } - - if (!view.exists(trustLineKey) && !receiverIssuer) - return tecNO_LINE; - - auto const xferRate = transferRate(view, amount); - // update if issuer rate is less than locked rate - if (xferRate < lockedRate) - lockedRate = xferRate; - - // Transfer Rate only applies when: - // 1. Issuer is not involved in the transfer (senderIssuer or - // receiverIssuer) - // 2. The locked rate is different from the parity rate - - // NOTE: Transfer fee in escrow works a bit differently from a normal - // payment. In escrow, the fee is deducted from the locked/sending amount, - // whereas in a normal payment, the transfer fee is taken on top of the - // sending amount. - auto finalAmt = amount; - if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate) - { - // compute transfer fee, if any - auto const xferFee = amount.value() - divideRound(amount, lockedRate, amount.issue(), true); - // compute balance to transfer - finalAmt = amount.value() - xferFee; - } - - // validate the line limit if the account submitting txn is not the receiver - // of the funds - if (!createAsset) - { - auto const sleRippleState = view.peek(trustLineKey); - if (!sleRippleState) - return tecINTERNAL; // LCOV_EXCL_LINE - - // if the issuer is the high, then we use the low limit - // otherwise we use the high limit - STAmount const lineLimit = - sleRippleState->getFieldAmount(issuerHigh ? sfLowLimit : sfHighLimit); - - STAmount lineBalance = sleRippleState->getFieldAmount(sfBalance); - - // flip the sign of the line balance if the issuer is not high - if (!issuerHigh) - lineBalance.negate(); - - // add the final amount to the line balance - lineBalance += finalAmt; - - // if the transfer would exceed the line limit return tecLIMIT_EXCEEDED - if (lineLimit < lineBalance) - return tecLIMIT_EXCEEDED; - } - - // if destination is not the issuer then transfer funds - if (!receiverIssuer) - { - auto const ter = rippleCredit(view, issuer, receiver, finalAmt, true, journal); - if (ter != tesSUCCESS) - return ter; // LCOV_EXCL_LINE - } - return tesSUCCESS; -} - -template <> -TER -escrowUnlockApplyHelper( - ApplyView& view, - Rate lockedRate, - std::shared_ptr const& sleDest, - STAmount const& xrpBalance, - STAmount const& amount, - AccountID const& issuer, - AccountID const& sender, - AccountID const& receiver, - bool createAsset, - beast::Journal journal) -{ - bool const senderIssuer = issuer == sender; - bool const receiverIssuer = issuer == receiver; - - auto const mptID = amount.get().getMptID(); - auto const issuanceKey = keylet::mptIssuance(mptID); - if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && createAsset && !receiverIssuer) - { - if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)}; - xrpBalance < view.fees().accountReserve(ownerCount + 1)) - { - return tecINSUFFICIENT_RESERVE; - } - - if (auto const ter = MPTokenAuthorize::createMPToken(view, mptID, receiver, 0); - !isTesSuccess(ter)) - { - return ter; // LCOV_EXCL_LINE - } - - // update owner count. - adjustOwnerCount(view, sleDest, 1, journal); - } - - if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && !receiverIssuer) - return tecNO_PERMISSION; - - auto const xferRate = transferRate(view, amount); - // update if issuer rate is less than locked rate - if (xferRate < lockedRate) - lockedRate = xferRate; - - // Transfer Rate only applies when: - // 1. Issuer is not involved in the transfer (senderIssuer or - // receiverIssuer) - // 2. The locked rate is different from the parity rate - - // NOTE: Transfer fee in escrow works a bit differently from a normal - // payment. In escrow, the fee is deducted from the locked/sending amount, - // whereas in a normal payment, the transfer fee is taken on top of the - // sending amount. - auto finalAmt = amount; - if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate) - { - // compute transfer fee, if any - auto const xferFee = amount.value() - divideRound(amount, lockedRate, amount.asset(), true); - // compute balance to transfer - finalAmt = amount.value() - xferFee; - } - return rippleUnlockEscrowMPT( - view, - sender, - receiver, - finalAmt, - view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt, - journal); -} - -TER -EscrowFinish::doApply() -{ - auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]); - auto const slep = ctx_.view().peek(k); - if (!slep) - { - if (ctx_.view().rules().enabled(featureTokenEscrow)) - return tecINTERNAL; // LCOV_EXCL_LINE - - return tecNO_TARGET; - } - - // If a cancel time is present, a finish operation should only succeed prior - // to that time. - auto const now = ctx_.view().header().parentCloseTime; - - // Too soon: can't execute before the finish time - if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter])) - return tecNO_PERMISSION; - - // Too late: can't execute after the cancel time - if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter])) - return tecNO_PERMISSION; - - // Check cryptocondition fulfillment - { - auto const id = ctx_.tx.getTransactionID(); - auto flags = ctx_.registry.getHashRouter().getFlags(id); - - auto const cb = ctx_.tx[~sfCondition]; - - // It's unlikely that the results of the check will - // expire from the hash router, but if it happens, - // simply re-run the check. - if (cb && !any(flags & (SF_CF_INVALID | SF_CF_VALID))) - { - // LCOV_EXCL_START - auto const fb = ctx_.tx[~sfFulfillment]; - - if (!fb) - return tecINTERNAL; - - if (checkCondition(*fb, *cb)) - flags = SF_CF_VALID; - else - flags = SF_CF_INVALID; - - ctx_.registry.getHashRouter().setFlags(id, flags); - // LCOV_EXCL_STOP - } - - // If the check failed, then simply return an error - // and don't look at anything else. - if (any(flags & SF_CF_INVALID)) - return tecCRYPTOCONDITION_ERROR; - - // Check against condition in the ledger entry: - auto const cond = (*slep)[~sfCondition]; - - // If a condition wasn't specified during creation, - // one shouldn't be included now. - if (!cond && cb) - return tecCRYPTOCONDITION_ERROR; - - // If a condition was specified during creation of - // the suspended payment, the identical condition - // must be presented again. We don't check if the - // fulfillment matches the condition since we did - // that in preflight. - if (cond && (cond != cb)) - return tecCRYPTOCONDITION_ERROR; - } - - // NOTE: Escrow payments cannot be used to fund accounts. - AccountID const destID = (*slep)[sfDestination]; - auto const sled = ctx_.view().peek(keylet::account(destID)); - if (!sled) - return tecNO_DST; - - if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), account_, destID, sled, ctx_.journal); - !isTesSuccess(err)) - return err; - - AccountID const account = (*slep)[sfAccount]; - - // Remove escrow from owner directory - { - auto const page = (*slep)[sfOwnerNode]; - if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, k.key, true)) - { - // LCOV_EXCL_START - JLOG(j_.fatal()) << "Unable to delete Escrow from owner."; - return tefBAD_LEDGER; - // LCOV_EXCL_STOP - } - } - - // Remove escrow from recipient's owner directory, if present. - if (auto const optPage = (*slep)[~sfDestinationNode]) - { - if (!ctx_.view().dirRemove(keylet::ownerDir(destID), *optPage, k.key, true)) - { - // LCOV_EXCL_START - JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; - return tefBAD_LEDGER; - // LCOV_EXCL_STOP - } - } - - STAmount const amount = slep->getFieldAmount(sfAmount); - // Transfer amount to destination - if (isXRP(amount)) - (*sled)[sfBalance] = (*sled)[sfBalance] + amount; - else - { - if (!ctx_.view().rules().enabled(featureTokenEscrow)) - return temDISABLED; // LCOV_EXCL_LINE - - Rate lockedRate = slep->isFieldPresent(sfTransferRate) - ? xrpl::Rate(slep->getFieldU32(sfTransferRate)) - : parityRate; - auto const issuer = amount.getIssuer(); - bool const createAsset = destID == account_; - if (auto const ret = std::visit( - [&](T const&) { - return escrowUnlockApplyHelper( - ctx_.view(), - lockedRate, - sled, - mPriorBalance, - amount, - issuer, - account, - destID, - createAsset, - j_); - }, - amount.asset().value()); - !isTesSuccess(ret)) - return ret; - - // Remove escrow from issuers owner directory, if present. - if (auto const optPage = (*slep)[~sfIssuerNode]; optPage) - { - if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true)) - { - // LCOV_EXCL_START - JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; - return tefBAD_LEDGER; - // LCOV_EXCL_STOP - } - } - } - - ctx_.view().update(sled); - - // Adjust source owner count - auto const sle = ctx_.view().peek(keylet::account(account)); - adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal); - ctx_.view().update(sle); - - // Remove escrow from ledger - ctx_.view().erase(slep); - return tesSUCCESS; -} - -//------------------------------------------------------------------------------ - -NotTEC -EscrowCancel::preflight(PreflightContext const& ctx) -{ - return tesSUCCESS; -} - -template -static TER -escrowCancelPreclaimHelper( - PreclaimContext const& ctx, - AccountID const& account, - STAmount const& amount); - -template <> -TER -escrowCancelPreclaimHelper( - PreclaimContext const& ctx, - AccountID const& account, - STAmount const& amount) -{ - AccountID issuer = amount.getIssuer(); - // If the issuer is the same as the account, return tecINTERNAL - if (issuer == account) - return tecINTERNAL; // LCOV_EXCL_LINE - - // If the issuer has requireAuth set, check if the account is authorized - if (auto const ter = requireAuth(ctx.view, amount.issue(), account); ter != tesSUCCESS) - return ter; - - return tesSUCCESS; -} - -template <> -TER -escrowCancelPreclaimHelper( - PreclaimContext const& ctx, - AccountID const& account, - STAmount const& amount) -{ - AccountID issuer = amount.getIssuer(); - // If the issuer is the same as the account, return tecINTERNAL - if (issuer == account) - return tecINTERNAL; // LCOV_EXCL_LINE - - // If the mpt does not exist, return tecOBJECT_NOT_FOUND - auto const issuanceKey = keylet::mptIssuance(amount.get().getMptID()); - auto const sleIssuance = ctx.view.read(issuanceKey); - if (!sleIssuance) - return tecOBJECT_NOT_FOUND; - - // If the issuer has requireAuth set, check if the account is - // authorized - auto const& mptIssue = amount.get(); - if (auto const ter = requireAuth(ctx.view, mptIssue, account, AuthType::WeakAuth); - ter != tesSUCCESS) - return ter; - - return tesSUCCESS; -} - -TER -EscrowCancel::preclaim(PreclaimContext const& ctx) -{ - if (ctx.view.rules().enabled(featureTokenEscrow)) - { - auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]); - auto const slep = ctx.view.read(k); - if (!slep) - return tecNO_TARGET; - - AccountID const account = (*slep)[sfAccount]; - STAmount const amount = (*slep)[sfAmount]; - - if (!isXRP(amount)) - { - if (auto const ret = std::visit( - [&](T const&) { - return escrowCancelPreclaimHelper(ctx, account, amount); - }, - amount.asset().value()); - !isTesSuccess(ret)) - return ret; - } - } - return tesSUCCESS; -} - -TER -EscrowCancel::doApply() -{ - auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]); - auto const slep = ctx_.view().peek(k); - if (!slep) - { - if (ctx_.view().rules().enabled(featureTokenEscrow)) - return tecINTERNAL; // LCOV_EXCL_LINE - - return tecNO_TARGET; - } - - auto const now = ctx_.view().header().parentCloseTime; - - // No cancel time specified: can't execute at all. - if (!(*slep)[~sfCancelAfter]) - return tecNO_PERMISSION; - - // Too soon: can't execute before the cancel time. - if (!after(now, (*slep)[sfCancelAfter])) - return tecNO_PERMISSION; - - AccountID const account = (*slep)[sfAccount]; - - // Remove escrow from owner directory - { - auto const page = (*slep)[sfOwnerNode]; - if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, k.key, true)) - { - // LCOV_EXCL_START - JLOG(j_.fatal()) << "Unable to delete Escrow from owner."; - return tefBAD_LEDGER; - // LCOV_EXCL_STOP - } - } - - // Remove escrow from recipient's owner directory, if present. - if (auto const optPage = (*slep)[~sfDestinationNode]; optPage) - { - if (!ctx_.view().dirRemove(keylet::ownerDir((*slep)[sfDestination]), *optPage, k.key, true)) - { - // LCOV_EXCL_START - JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; - return tefBAD_LEDGER; - // LCOV_EXCL_STOP - } - } - - auto const sle = ctx_.view().peek(keylet::account(account)); - STAmount const amount = slep->getFieldAmount(sfAmount); - - // Transfer amount back to the owner - if (isXRP(amount)) - (*sle)[sfBalance] = (*sle)[sfBalance] + amount; - else - { - if (!ctx_.view().rules().enabled(featureTokenEscrow)) - return temDISABLED; // LCOV_EXCL_LINE - - auto const issuer = amount.getIssuer(); - bool const createAsset = account == account_; - if (auto const ret = std::visit( - [&](T const&) { - return escrowUnlockApplyHelper( - ctx_.view(), - parityRate, - slep, - mPriorBalance, - amount, - issuer, - account, // sender and receiver are the same - account, - createAsset, - j_); - }, - amount.asset().value()); - !isTesSuccess(ret)) - return ret; // LCOV_EXCL_LINE - - // Remove escrow from issuers owner directory, if present. - if (auto const optPage = (*slep)[~sfIssuerNode]; optPage) - { - if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true)) - { - // LCOV_EXCL_START - JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; - return tefBAD_LEDGER; - // LCOV_EXCL_STOP - } - } - } - - adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal); - ctx_.view().update(sle); - - // Remove escrow from ledger - ctx_.view().erase(slep); - - return tesSUCCESS; -} - -} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp new file mode 100644 index 0000000000..cd024a076d --- /dev/null +++ b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +NotTEC +EscrowCancel::preflight(PreflightContext const& ctx) +{ + return tesSUCCESS; +} + +template +static TER +escrowCancelPreclaimHelper( + PreclaimContext const& ctx, + AccountID const& account, + STAmount const& amount); + +template <> +TER +escrowCancelPreclaimHelper( + PreclaimContext const& ctx, + AccountID const& account, + STAmount const& amount) +{ + AccountID const issuer = amount.getIssuer(); + // If the issuer is the same as the account, return tecINTERNAL + if (issuer == account) + return tecINTERNAL; // LCOV_EXCL_LINE + + // If the issuer has requireAuth set, check if the account is authorized + if (auto const ter = requireAuth(ctx.view, amount.issue(), account); !isTesSuccess(ter)) + return ter; + + return tesSUCCESS; +} + +template <> +TER +escrowCancelPreclaimHelper( + PreclaimContext const& ctx, + AccountID const& account, + STAmount const& amount) +{ + AccountID const issuer = amount.getIssuer(); + // If the issuer is the same as the account, return tecINTERNAL + if (issuer == account) + return tecINTERNAL; // LCOV_EXCL_LINE + + // If the mpt does not exist, return tecOBJECT_NOT_FOUND + auto const issuanceKey = keylet::mptIssuance(amount.get().getMptID()); + auto const sleIssuance = ctx.view.read(issuanceKey); + if (!sleIssuance) + return tecOBJECT_NOT_FOUND; + + // If the issuer has requireAuth set, check if the account is + // authorized + auto const& mptIssue = amount.get(); + if (auto const ter = requireAuth(ctx.view, mptIssue, account, AuthType::WeakAuth); + !isTesSuccess(ter)) + return ter; + + return tesSUCCESS; +} + +TER +EscrowCancel::preclaim(PreclaimContext const& ctx) +{ + if (ctx.view.rules().enabled(featureTokenEscrow)) + { + auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]); + auto const slep = ctx.view.read(k); + if (!slep) + return tecNO_TARGET; + + AccountID const account = (*slep)[sfAccount]; + STAmount const amount = (*slep)[sfAmount]; + + if (!isXRP(amount)) + { + if (auto const ret = std::visit( + [&](T const&) { + return escrowCancelPreclaimHelper(ctx, account, amount); + }, + amount.asset().value()); + !isTesSuccess(ret)) + return ret; + } + } + return tesSUCCESS; +} + +TER +EscrowCancel::doApply() +{ + auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]); + auto const slep = ctx_.view().peek(k); + if (!slep) + { + if (ctx_.view().rules().enabled(featureTokenEscrow)) + return tecINTERNAL; // LCOV_EXCL_LINE + + return tecNO_TARGET; + } + + auto const now = ctx_.view().header().parentCloseTime; + + // No cancel time specified: can't execute at all. + if (!(*slep)[~sfCancelAfter]) + return tecNO_PERMISSION; + + // Too soon: can't execute before the cancel time. + if (!after(now, (*slep)[sfCancelAfter])) + return tecNO_PERMISSION; + + AccountID const account = (*slep)[sfAccount]; + + // Remove escrow from owner directory + { + auto const page = (*slep)[sfOwnerNode]; + if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, k.key, true)) + { + // LCOV_EXCL_START + JLOG(j_.fatal()) << "Unable to delete Escrow from owner."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + + // Remove escrow from recipient's owner directory, if present. + if (auto const optPage = (*slep)[~sfDestinationNode]; optPage) + { + if (!ctx_.view().dirRemove(keylet::ownerDir((*slep)[sfDestination]), *optPage, k.key, true)) + { + // LCOV_EXCL_START + JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + + auto const sle = ctx_.view().peek(keylet::account(account)); + STAmount const amount = slep->getFieldAmount(sfAmount); + + // Transfer amount back to the owner + if (isXRP(amount)) + { + (*sle)[sfBalance] = (*sle)[sfBalance] + amount; + } + else + { + if (!ctx_.view().rules().enabled(featureTokenEscrow)) + return temDISABLED; // LCOV_EXCL_LINE + + auto const issuer = amount.getIssuer(); + bool const createAsset = account == account_; + if (auto const ret = std::visit( + [&](T const&) { + return escrowUnlockApplyHelper( + ctx_.view(), + parityRate, + slep, + preFeeBalance_, + amount, + issuer, + account, // sender and receiver are the same + account, + createAsset, + j_); + }, + amount.asset().value()); + !isTesSuccess(ret)) + return ret; // LCOV_EXCL_LINE + + // Remove escrow from issuers owner directory, if present. + if (auto const optPage = (*slep)[~sfIssuerNode]; optPage) + { + if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true)) + { + // LCOV_EXCL_START + JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + } + + adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal); + ctx_.view().update(sle); + + // Remove escrow from ledger + ctx_.view().erase(slep); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp new file mode 100644 index 0000000000..a14b3abf37 --- /dev/null +++ b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp @@ -0,0 +1,506 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +/* + Escrow + ====== + + Escrow is a feature of the XRP Ledger that allows you to send conditional + XRP payments. These conditional payments, called escrows, set aside XRP and + deliver it later when certain conditions are met. Conditions to successfully + finish an escrow include time-based unlocks and crypto-conditions. Escrows + can also be set to expire if not finished in time. + + The XRP set aside in an escrow is locked up. No one can use or destroy the + XRP until the escrow has been successfully finished or canceled. Before the + expiration time, only the intended receiver can get the XRP. After the + expiration time, the XRP can only be returned to the sender. + + For more details on escrow, including examples, diagrams and more please + visit https://xrpl.org/escrow.html + + For details on specific transactions, including fields and validation rules + please see: + + `EscrowCreate` + -------------- + See: https://xrpl.org/escrowcreate.html + + `EscrowFinish` + -------------- + See: https://xrpl.org/escrowfinish.html + + `EscrowCancel` + -------------- + See: https://xrpl.org/escrowcancel.html +*/ + +//------------------------------------------------------------------------------ + +TxConsequences +EscrowCreate::makeTxConsequences(PreflightContext const& ctx) +{ + auto const amount = ctx.tx[sfAmount]; + return TxConsequences{ctx.tx, isXRP(amount) ? amount.xrp() : beast::zero}; +} + +template +static NotTEC +escrowCreatePreflightHelper(PreflightContext const& ctx); + +template <> +NotTEC +escrowCreatePreflightHelper(PreflightContext const& ctx) +{ + STAmount const amount = ctx.tx[sfAmount]; + if (amount.native() || amount <= beast::zero) + return temBAD_AMOUNT; + + if (badCurrency() == amount.getCurrency()) + return temBAD_CURRENCY; + + return tesSUCCESS; +} + +template <> +NotTEC +escrowCreatePreflightHelper(PreflightContext const& ctx) +{ + if (!ctx.rules.enabled(featureMPTokensV1)) + return temDISABLED; + + auto const amount = ctx.tx[sfAmount]; + if (amount.native() || amount.mpt() > MPTAmount{maxMPTokenAmount} || amount <= beast::zero) + return temBAD_AMOUNT; + + return tesSUCCESS; +} + +NotTEC +EscrowCreate::preflight(PreflightContext const& ctx) +{ + STAmount const amount{ctx.tx[sfAmount]}; + if (!isXRP(amount)) + { + if (!ctx.rules.enabled(featureTokenEscrow)) + return temBAD_AMOUNT; + + if (auto const ret = std::visit( + [&](T const&) { return escrowCreatePreflightHelper(ctx); }, + amount.asset().value()); + !isTesSuccess(ret)) + return ret; + } + else + { + if (amount <= beast::zero) + return temBAD_AMOUNT; + } + + // We must specify at least one timeout value + if (!ctx.tx[~sfCancelAfter] && !ctx.tx[~sfFinishAfter]) + return temBAD_EXPIRATION; + + // If both finish and cancel times are specified then the cancel time must + // be strictly after the finish time. + if (ctx.tx[~sfCancelAfter] && ctx.tx[~sfFinishAfter] && + ctx.tx[sfCancelAfter] <= ctx.tx[sfFinishAfter]) + return temBAD_EXPIRATION; + + // In the absence of a FinishAfter, the escrow can be finished + // immediately, which can be confusing. When creating an escrow, + // we want to ensure that either a FinishAfter time is explicitly + // specified or a completion condition is attached. + if (!ctx.tx[~sfFinishAfter] && !ctx.tx[~sfCondition]) + return temMALFORMED; + + if (auto const cb = ctx.tx[~sfCondition]) + { + using namespace xrpl::cryptoconditions; + + std::error_code ec; + + auto condition = Condition::deserialize(*cb, ec); + if (!condition) + { + JLOG(ctx.j.debug()) << "Malformed condition during escrow creation: " << ec.message(); + return temMALFORMED; + } + } + + return tesSUCCESS; +} + +template +static TER +escrowCreatePreclaimHelper( + PreclaimContext const& ctx, + AccountID const& account, + AccountID const& dest, + STAmount const& amount); + +template <> +TER +escrowCreatePreclaimHelper( + PreclaimContext const& ctx, + AccountID const& account, + AccountID const& dest, + STAmount const& amount) +{ + AccountID const issuer = amount.getIssuer(); + // If the issuer is the same as the account, return tecNO_PERMISSION + if (issuer == account) + return tecNO_PERMISSION; + + // If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION + auto const sleIssuer = ctx.view.read(keylet::account(issuer)); + if (!sleIssuer) + return tecNO_ISSUER; + if (!sleIssuer->isFlag(lsfAllowTrustLineLocking)) + return tecNO_PERMISSION; + + // If the account does not have a trustline to the issuer, return tecNO_LINE + auto const sleRippleState = ctx.view.read(keylet::line(account, issuer, amount.getCurrency())); + if (!sleRippleState) + return tecNO_LINE; + + STAmount const balance = (*sleRippleState)[sfBalance]; + + // If balance is positive, issuer must have higher address than account + if (balance > beast::zero && issuer < account) + return tecNO_PERMISSION; // LCOV_EXCL_LINE + + // If balance is negative, issuer must have lower address than account + if (balance < beast::zero && issuer > account) + return tecNO_PERMISSION; // LCOV_EXCL_LINE + + // If the issuer has requireAuth set, check if the account is authorized + if (auto const ter = requireAuth(ctx.view, amount.issue(), account); !isTesSuccess(ter)) + return ter; + + // If the issuer has requireAuth set, check if the destination is authorized + if (auto const ter = requireAuth(ctx.view, amount.issue(), dest); !isTesSuccess(ter)) + return ter; + + // If the issuer has frozen the account, return tecFROZEN + if (isFrozen(ctx.view, account, amount.issue())) + return tecFROZEN; + + // If the issuer has frozen the destination, return tecFROZEN + if (isFrozen(ctx.view, dest, amount.issue())) + return tecFROZEN; + + STAmount const spendableAmount = + accountHolds(ctx.view, account, amount.getCurrency(), issuer, fhIGNORE_FREEZE, ctx.j); + + // If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS + if (spendableAmount <= beast::zero) + return tecINSUFFICIENT_FUNDS; + + // If the spendable amount is less than the amount, return + // tecINSUFFICIENT_FUNDS + if (spendableAmount < amount) + return tecINSUFFICIENT_FUNDS; + + // If the amount is not addable to the balance, return tecPRECISION_LOSS + if (!canAdd(spendableAmount, amount)) + return tecPRECISION_LOSS; + + return tesSUCCESS; +} + +template <> +TER +escrowCreatePreclaimHelper( + PreclaimContext const& ctx, + AccountID const& account, + AccountID const& dest, + STAmount const& amount) +{ + AccountID const issuer = amount.getIssuer(); + // If the issuer is the same as the account, return tecNO_PERMISSION + if (issuer == account) + return tecNO_PERMISSION; + + // If the mpt does not exist, return tecOBJECT_NOT_FOUND + auto const issuanceKey = keylet::mptIssuance(amount.get().getMptID()); + auto const sleIssuance = ctx.view.read(issuanceKey); + if (!sleIssuance) + return tecOBJECT_NOT_FOUND; + + // If the lsfMPTCanEscrow is not enabled, return tecNO_PERMISSION + if (!sleIssuance->isFlag(lsfMPTCanEscrow)) + return tecNO_PERMISSION; + + // If the issuer is not the same as the issuer of the mpt, return + // tecNO_PERMISSION + if (sleIssuance->getAccountID(sfIssuer) != issuer) + return tecNO_PERMISSION; // LCOV_EXCL_LINE + + // If the account does not have the mpt, return tecOBJECT_NOT_FOUND + if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, account))) + return tecOBJECT_NOT_FOUND; + + // If the issuer has requireAuth set, check if the account is + // authorized + auto const& mptIssue = amount.get(); + if (auto const ter = requireAuth(ctx.view, mptIssue, account, AuthType::WeakAuth); + !isTesSuccess(ter)) + return ter; + + // If the issuer has requireAuth set, check if the destination is + // authorized + if (auto const ter = requireAuth(ctx.view, mptIssue, dest, AuthType::WeakAuth); + !isTesSuccess(ter)) + return ter; + + // If the issuer has frozen the account, return tecLOCKED + if (isFrozen(ctx.view, account, mptIssue)) + return tecLOCKED; + + // If the issuer has frozen the destination, return tecLOCKED + if (isFrozen(ctx.view, dest, mptIssue)) + return tecLOCKED; + + // If the mpt cannot be transferred, return tecNO_AUTH + if (auto const ter = canTransfer(ctx.view, mptIssue, account, dest); !isTesSuccess(ter)) + return ter; + + STAmount const spendableAmount = accountHolds( + ctx.view, account, amount.get(), fhIGNORE_FREEZE, ahIGNORE_AUTH, ctx.j); + + // If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS + if (spendableAmount <= beast::zero) + return tecINSUFFICIENT_FUNDS; + + // If the spendable amount is less than the amount, return + // tecINSUFFICIENT_FUNDS + if (spendableAmount < amount) + return tecINSUFFICIENT_FUNDS; + + return tesSUCCESS; +} + +TER +EscrowCreate::preclaim(PreclaimContext const& ctx) +{ + STAmount const amount{ctx.tx[sfAmount]}; + AccountID const account{ctx.tx[sfAccount]}; + AccountID const dest{ctx.tx[sfDestination]}; + + auto const sled = ctx.view.read(keylet::account(dest)); + if (!sled) + return tecNO_DST; + + // Pseudo-accounts cannot receive escrow. Note, this is not amendment-gated + // because all writes to pseudo-account discriminator fields **are** + // amendment gated, hence the behaviour of this check will always match the + // currently active amendments. + if (isPseudoAccount(sled)) + return tecNO_PERMISSION; + + if (!isXRP(amount)) + { + if (!ctx.view.rules().enabled(featureTokenEscrow)) + return temDISABLED; // LCOV_EXCL_LINE + + if (auto const ret = std::visit( + [&](T const&) { + return escrowCreatePreclaimHelper(ctx, account, dest, amount); + }, + amount.asset().value()); + !isTesSuccess(ret)) + return ret; + } + return tesSUCCESS; +} + +template +static TER +escrowLockApplyHelper( + ApplyView& view, + AccountID const& issuer, + AccountID const& sender, + STAmount const& amount, + beast::Journal journal); + +template <> +TER +escrowLockApplyHelper( + ApplyView& view, + AccountID const& issuer, + AccountID const& sender, + STAmount const& amount, + beast::Journal journal) +{ + // Defensive: Issuer cannot create an escrow + if (issuer == sender) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const ter = + directSendNoFee(view, sender, issuer, amount, !amount.holds(), journal); + if (!isTesSuccess(ter)) + return ter; // LCOV_EXCL_LINE + return tesSUCCESS; +} + +template <> +TER +escrowLockApplyHelper( + ApplyView& view, + AccountID const& issuer, + AccountID const& sender, + STAmount const& amount, + beast::Journal journal) +{ + // Defensive: Issuer cannot create an escrow + if (issuer == sender) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const ter = lockEscrowMPT(view, sender, amount, journal); + if (!isTesSuccess(ter)) + return ter; // LCOV_EXCL_LINE + return tesSUCCESS; +} + +TER +EscrowCreate::doApply() +{ + auto const closeTime = ctx_.view().header().parentCloseTime; + + if (ctx_.tx[~sfCancelAfter] && after(closeTime, ctx_.tx[sfCancelAfter])) + return tecNO_PERMISSION; + + if (ctx_.tx[~sfFinishAfter] && after(closeTime, ctx_.tx[sfFinishAfter])) + return tecNO_PERMISSION; + + auto const sle = ctx_.view().peek(keylet::account(account_)); + if (!sle) + return tefINTERNAL; // LCOV_EXCL_LINE + + // Check reserve and funds availability + STAmount const amount{ctx_.tx[sfAmount]}; + + auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1); + + auto const balance = sle->getFieldAmount(sfBalance).xrp(); + if (balance < reserve) + return tecINSUFFICIENT_RESERVE; + + // Check reserve and funds availability + if (isXRP(amount)) + { + if (balance < reserve + STAmount(amount).xrp()) + return tecUNFUNDED; + } + + // Check destination account + { + auto const sled = ctx_.view().read(keylet::account(ctx_.tx[sfDestination])); + if (!sled) + return tecNO_DST; // LCOV_EXCL_LINE + if ((((*sled)[sfFlags] & lsfRequireDestTag) != 0u) && !ctx_.tx[~sfDestinationTag]) + return tecDST_TAG_NEEDED; + } + + // Create escrow in ledger. Note that we we use the value from the + // sequence or ticket. For more explanation see comments in SeqProxy.h. + Keylet const escrowKeylet = keylet::escrow(account_, ctx_.tx.getSeqValue()); + auto const slep = std::make_shared(escrowKeylet); + (*slep)[sfAmount] = amount; + (*slep)[sfAccount] = account_; + (*slep)[~sfCondition] = ctx_.tx[~sfCondition]; + (*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag]; + (*slep)[sfDestination] = ctx_.tx[sfDestination]; + (*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter]; + (*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter]; + (*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag]; + + if (ctx_.view().rules().enabled(fixIncludeKeyletFields)) + { + (*slep)[sfSequence] = ctx_.tx.getSeqValue(); + } + + if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount)) + { + auto const xferRate = transferRate(ctx_.view(), amount); + if (xferRate != parityRate) + (*slep)[sfTransferRate] = xferRate.value; + } + + ctx_.view().insert(slep); + + // Add escrow to sender's owner directory + { + auto page = ctx_.view().dirInsert( + keylet::ownerDir(account_), escrowKeylet, describeOwnerDir(account_)); + if (!page) + return tecDIR_FULL; // LCOV_EXCL_LINE + (*slep)[sfOwnerNode] = *page; + } + + // If it's not a self-send, add escrow to recipient's owner directory. + AccountID const dest = ctx_.tx[sfDestination]; + if (dest != account_) + { + auto page = + ctx_.view().dirInsert(keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest)); + if (!page) + return tecDIR_FULL; // LCOV_EXCL_LINE + (*slep)[sfDestinationNode] = *page; + } + + // IOU escrow objects are added to the issuer's owner directory to help + // track the total locked balance. For MPT, this isn't necessary because the + // locked balance is already stored directly in the MPTokenIssuance object. + AccountID const issuer = amount.getIssuer(); + if (!isXRP(amount) && issuer != account_ && issuer != dest && !amount.holds()) + { + auto page = + ctx_.view().dirInsert(keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer)); + if (!page) + return tecDIR_FULL; // LCOV_EXCL_LINE + (*slep)[sfIssuerNode] = *page; + } + + // Deduct owner's balance + if (isXRP(amount)) + { + (*sle)[sfBalance] = (*sle)[sfBalance] - amount; + } + else + { + if (auto const ret = std::visit( + [&](T const&) { + return escrowLockApplyHelper(ctx_.view(), issuer, account_, amount, j_); + }, + amount.asset().value()); + !isTesSuccess(ret)) + { + return ret; // LCOV_EXCL_LINE + } + } + + // increment owner count + adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); + ctx_.view().update(sle); + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp new file mode 100644 index 0000000000..5c28fa5dd3 --- /dev/null +++ b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp @@ -0,0 +1,384 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +// During an EscrowFinish, the transaction must specify both +// a condition and a fulfillment. We track whether that +// fulfillment matches and validates the condition. +constexpr HashRouterFlags SF_CF_INVALID = HashRouterFlags::PRIVATE5; +constexpr HashRouterFlags SF_CF_VALID = HashRouterFlags::PRIVATE6; + +//------------------------------------------------------------------------------ + +static bool +checkCondition(Slice f, Slice c) +{ + using namespace xrpl::cryptoconditions; + + std::error_code ec; + + auto condition = Condition::deserialize(c, ec); + if (!condition) + return false; + + auto fulfillment = Fulfillment::deserialize(f, ec); + if (!fulfillment) + return false; + + return validate(*fulfillment, *condition); +} + +bool +EscrowFinish::checkExtraFeatures(PreflightContext const& ctx) +{ + return !ctx.tx.isFieldPresent(sfCredentialIDs) || ctx.rules.enabled(featureCredentials); +} + +NotTEC +EscrowFinish::preflight(PreflightContext const& ctx) +{ + auto const cb = ctx.tx[~sfCondition]; + auto const fb = ctx.tx[~sfFulfillment]; + + // If you specify a condition, then you must also specify + // a fulfillment. + if (static_cast(cb) != static_cast(fb)) + return temMALFORMED; + + return tesSUCCESS; +} + +NotTEC +EscrowFinish::preflightSigValidated(PreflightContext const& ctx) +{ + auto const cb = ctx.tx[~sfCondition]; + auto const fb = ctx.tx[~sfFulfillment]; + + if (cb && fb) + { + auto& router = ctx.registry.get().getHashRouter(); + + auto const id = ctx.tx.getTransactionID(); + auto const flags = router.getFlags(id); + + // If we haven't checked the condition, check it + // now. Whether it passes or not isn't important + // in preflight. + if (!any(flags & (SF_CF_INVALID | SF_CF_VALID))) + { + if (checkCondition(*fb, *cb)) + { + router.setFlags(id, SF_CF_VALID); + } + else + { + router.setFlags(id, SF_CF_INVALID); + } + } + } + + if (auto const err = credentials::checkFields(ctx.tx, ctx.j); !isTesSuccess(err)) + return err; + + return tesSUCCESS; +} + +XRPAmount +EscrowFinish::calculateBaseFee(ReadView const& view, STTx const& tx) +{ + XRPAmount extraFee{0}; + + if (auto const fb = tx[~sfFulfillment]) + { + extraFee += view.fees().base * (32 + (fb->size() / 16)); + } + + return Transactor::calculateBaseFee(view, tx) + extraFee; +} + +template +static TER +escrowFinishPreclaimHelper( + PreclaimContext const& ctx, + AccountID const& dest, + STAmount const& amount); + +template <> +TER +escrowFinishPreclaimHelper( + PreclaimContext const& ctx, + AccountID const& dest, + STAmount const& amount) +{ + AccountID const issuer = amount.getIssuer(); + // If the issuer is the same as the account, return tesSUCCESS + if (issuer == dest) + return tesSUCCESS; + + // If the issuer has requireAuth set, check if the destination is authorized + if (auto const ter = requireAuth(ctx.view, amount.issue(), dest); !isTesSuccess(ter)) + return ter; + + // If the issuer has deep frozen the destination, return tecFROZEN + if (isDeepFrozen(ctx.view, dest, amount.getCurrency(), amount.getIssuer())) + return tecFROZEN; + + return tesSUCCESS; +} + +template <> +TER +escrowFinishPreclaimHelper( + PreclaimContext const& ctx, + AccountID const& dest, + STAmount const& amount) +{ + AccountID const issuer = amount.getIssuer(); + // If the issuer is the same as the dest, return tesSUCCESS + if (issuer == dest) + return tesSUCCESS; + + // If the mpt does not exist, return tecOBJECT_NOT_FOUND + auto const issuanceKey = keylet::mptIssuance(amount.get().getMptID()); + auto const sleIssuance = ctx.view.read(issuanceKey); + if (!sleIssuance) + return tecOBJECT_NOT_FOUND; + + // If the issuer has requireAuth set, check if the destination is + // authorized + auto const& mptIssue = amount.get(); + if (auto const ter = requireAuth(ctx.view, mptIssue, dest, AuthType::WeakAuth); + !isTesSuccess(ter)) + return ter; + + // If the issuer has frozen the destination, return tecLOCKED + if (isFrozen(ctx.view, dest, mptIssue)) + return tecLOCKED; + + return tesSUCCESS; +} + +TER +EscrowFinish::preclaim(PreclaimContext const& ctx) +{ + if (ctx.view.rules().enabled(featureCredentials)) + { + if (auto const err = credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j); + !isTesSuccess(err)) + return err; + } + + if (ctx.view.rules().enabled(featureTokenEscrow)) + { + auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]); + auto const slep = ctx.view.read(k); + if (!slep) + return tecNO_TARGET; + + AccountID const dest = (*slep)[sfDestination]; + STAmount const amount = (*slep)[sfAmount]; + + if (!isXRP(amount)) + { + if (auto const ret = std::visit( + [&](T const&) { + return escrowFinishPreclaimHelper(ctx, dest, amount); + }, + amount.asset().value()); + !isTesSuccess(ret)) + return ret; + } + } + return tesSUCCESS; +} + +TER +EscrowFinish::doApply() +{ + auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]); + auto const slep = ctx_.view().peek(k); + if (!slep) + { + if (ctx_.view().rules().enabled(featureTokenEscrow)) + return tecINTERNAL; // LCOV_EXCL_LINE + + return tecNO_TARGET; + } + + // If a cancel time is present, a finish operation should only succeed prior + // to that time. + auto const now = ctx_.view().header().parentCloseTime; + + // Too soon: can't execute before the finish time + if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter])) + return tecNO_PERMISSION; + + // Too late: can't execute after the cancel time + if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter])) + return tecNO_PERMISSION; + + // Check cryptocondition fulfillment + { + auto const id = ctx_.tx.getTransactionID(); + auto flags = ctx_.registry.get().getHashRouter().getFlags(id); + + auto const cb = ctx_.tx[~sfCondition]; + + // It's unlikely that the results of the check will + // expire from the hash router, but if it happens, + // simply re-run the check. + if (cb && !any(flags & (SF_CF_INVALID | SF_CF_VALID))) + { + // LCOV_EXCL_START + auto const fb = ctx_.tx[~sfFulfillment]; + + if (!fb) + return tecINTERNAL; + + if (checkCondition(*fb, *cb)) + { + flags = SF_CF_VALID; + } + else + { + flags = SF_CF_INVALID; + } + + ctx_.registry.get().getHashRouter().setFlags(id, flags); + // LCOV_EXCL_STOP + } + + // If the check failed, then simply return an error + // and don't look at anything else. + if (any(flags & SF_CF_INVALID)) + return tecCRYPTOCONDITION_ERROR; + + // Check against condition in the ledger entry: + auto const cond = (*slep)[~sfCondition]; + + // If a condition wasn't specified during creation, + // one shouldn't be included now. + if (!cond && cb) + return tecCRYPTOCONDITION_ERROR; + + // If a condition was specified during creation of + // the suspended payment, the identical condition + // must be presented again. We don't check if the + // fulfillment matches the condition since we did + // that in preflight. + if (cond && (cond != cb)) + return tecCRYPTOCONDITION_ERROR; + } + + // NOTE: Escrow payments cannot be used to fund accounts. + AccountID const destID = (*slep)[sfDestination]; + auto const sled = ctx_.view().peek(keylet::account(destID)); + if (!sled) + return tecNO_DST; + + if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), account_, destID, sled, ctx_.journal); + !isTesSuccess(err)) + return err; + + AccountID const account = (*slep)[sfAccount]; + + // Remove escrow from owner directory + { + auto const page = (*slep)[sfOwnerNode]; + if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, k.key, true)) + { + // LCOV_EXCL_START + JLOG(j_.fatal()) << "Unable to delete Escrow from owner."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + + // Remove escrow from recipient's owner directory, if present. + if (auto const optPage = (*slep)[~sfDestinationNode]) + { + if (!ctx_.view().dirRemove(keylet::ownerDir(destID), *optPage, k.key, true)) + { + // LCOV_EXCL_START + JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + + STAmount const amount = slep->getFieldAmount(sfAmount); + // Transfer amount to destination + if (isXRP(amount)) + { + (*sled)[sfBalance] = (*sled)[sfBalance] + amount; + } + else + { + if (!ctx_.view().rules().enabled(featureTokenEscrow)) + return temDISABLED; // LCOV_EXCL_LINE + + Rate lockedRate = slep->isFieldPresent(sfTransferRate) + ? xrpl::Rate(slep->getFieldU32(sfTransferRate)) + : parityRate; + auto const issuer = amount.getIssuer(); + bool const createAsset = destID == account_; + if (auto const ret = std::visit( + [&](T const&) { + return escrowUnlockApplyHelper( + ctx_.view(), + lockedRate, + sled, + preFeeBalance_, + amount, + issuer, + account, + destID, + createAsset, + j_); + }, + amount.asset().value()); + !isTesSuccess(ret)) + return ret; + + // Remove escrow from issuers owner directory, if present. + if (auto const optPage = (*slep)[~sfIssuerNode]; optPage) + { + if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true)) + { + // LCOV_EXCL_START + JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + } + + ctx_.view().update(sled); + + // Adjust source owner count + auto const sle = ctx_.view().peek(keylet::account(account)); + adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal); + ctx_.view().update(sle); + + // Remove escrow from ledger + ctx_.view().erase(slep); + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/lending/LendingHelpers.cpp b/src/libxrpl/tx/transactors/lending/LendingHelpers.cpp index d7162bbb0e..bad55e9222 100644 --- a/src/libxrpl/tx/transactors/lending/LendingHelpers.cpp +++ b/src/libxrpl/tx/transactors/lending/LendingHelpers.cpp @@ -856,9 +856,11 @@ computeFullPayment( << ", untrackedInterest: " << full.untrackedInterest; if (amount < full.totalDue) + { // If the payment is less than the full payment amount, it's not // sufficient to be a full payment. return Unexpected(tecINSUFFICIENT_PAYMENT); + } return full; } @@ -1251,7 +1253,7 @@ checkLoanGuards( // loan can't be amortized in the specified number of payments, raise an // error { - NumberRoundModeGuard mg(Number::upward); + NumberRoundModeGuard const mg(Number::upward); if (std::int64_t const computedPayments{ properties.loanState.valueOutstanding / roundedPayment}; @@ -1484,7 +1486,7 @@ computeLoanProperties( auto const [totalValueOutstanding, loanScale] = [&]() { // only round up if there should be interest - NumberRoundModeGuard mg(periodicRate == 0 ? Number::to_nearest : Number::upward); + NumberRoundModeGuard const mg(periodicRate == 0 ? Number::to_nearest : Number::upward); // Use STAmount's internal rounding instead of roundToAsset, because // we're going to use this result to determine the scale for all the // other rounding. @@ -1635,24 +1637,26 @@ loanMakePayment( LoanState const roundedLoanState = constructLoanState( totalValueOutstandingProxy, principalOutstandingProxy, managementFeeOutstandingProxy); - if (auto const fullPaymentComponents = detail::computeFullPayment( - asset, - view, - principalOutstandingProxy, - managementFeeOutstandingProxy, - periodicPayment, - paymentRemainingProxy, - prevPaymentDateProxy, - startDate, - paymentInterval, - closeInterestRate, - loanScale, - roundedLoanState.interestDue, - periodicRate, - closePaymentFee, - amount, - managementFeeRate, - j)) + auto const fullPaymentComponents = detail::computeFullPayment( + asset, + view, + principalOutstandingProxy, + managementFeeOutstandingProxy, + periodicPayment, + paymentRemainingProxy, + prevPaymentDateProxy, + startDate, + paymentInterval, + closeInterestRate, + loanScale, + roundedLoanState.interestDue, + periodicRate, + closePaymentFee, + amount, + managementFeeRate, + j); + + if (fullPaymentComponents.has_value()) { return doPayment( *fullPaymentComponents, @@ -1664,11 +1668,14 @@ loanMakePayment( nextDueDateProxy, paymentInterval); } - else if (fullPaymentComponents.error()) + + if (fullPaymentComponents.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(fullPaymentComponents.error()); + } // LCOV_EXCL_START UNREACHABLE("xrpl::loanMakePayment : invalid full payment result"); @@ -1704,18 +1711,20 @@ loanMakePayment( TenthBips32 const lateInterestRate{loan->at(sfLateInterestRate)}; Number const latePaymentFee = loan->at(sfLatePaymentFee); - if (auto const latePaymentComponents = detail::computeLatePayment( - asset, - view, - principalOutstandingProxy, - nextDueDateProxy, - periodic, - lateInterestRate, - loanScale, - latePaymentFee, - amount, - managementFeeRate, - j)) + auto const latePaymentComponents = detail::computeLatePayment( + asset, + view, + principalOutstandingProxy, + nextDueDateProxy, + periodic, + lateInterestRate, + loanScale, + latePaymentFee, + amount, + managementFeeRate, + j); + + if (latePaymentComponents.has_value()) { return doPayment( *latePaymentComponents, @@ -1727,7 +1736,8 @@ loanMakePayment( nextDueDateProxy, paymentInterval); } - else if (latePaymentComponents.error()) + + if (latePaymentComponents.error()) { // error() will be the TER returned if a payment is not made. It // will only evaluate to true if it's unsuccessful. @@ -1858,13 +1868,17 @@ loanMakePayment( 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/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp index 8513ae5999..627b16794b 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp @@ -1,5 +1,6 @@ #include // +#include #include #include @@ -118,14 +119,14 @@ determineAsset( { return amount.asset(); } - else if (holder == brokerPseudoAccountID) + if (holder == brokerPseudoAccountID) { // We want the asset to match the vault asset, so use the account as the // issuer return Issue{amount.getCurrency(), account}; } - else - return Unexpected(tecWRONG_ASSET); + + return Unexpected(tecWRONG_ASSET); } Expected @@ -136,11 +137,11 @@ determineClawAmount( { auto const maxClawAmount = [&]() { // Always round the minimum required up - NumberRoundModeGuard mg1(Number::upward); + NumberRoundModeGuard const mg1(Number::upward); auto const minRequiredCover = tenthBipsOfValue(sleBroker[sfDebtTotal], TenthBips32(sleBroker[sfCoverRateMinimum])); // The subtraction probably won't round, but round down if it does. - NumberRoundModeGuard mg2(Number::downward); + NumberRoundModeGuard const mg2(Number::downward); return sleBroker[sfCoverAvailable] - minRequiredCover; }(); if (maxClawAmount <= beast::zero) diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp index 83f7d2d1c6..4630e6a360 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp @@ -1,5 +1,6 @@ #include // +#include #include #include diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp index 9140a0dc23..d03edad0a2 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp @@ -1,6 +1,8 @@ #include // -#include +#include +#include +#include #include #include #include @@ -117,7 +119,7 @@ LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx) auto const minimumCover = [&]() { // Always round the minimum required up. // Applies to `tenthBipsOfValue` as well as `roundToAsset`. - NumberRoundModeGuard mg(Number::upward); + NumberRoundModeGuard const mg(Number::upward); return roundToAsset( vaultAsset, tenthBipsOfValue(currentDebtTotal, TenthBips32(sleBroker->at(sfCoverRateMinimum))), @@ -167,7 +169,7 @@ LoanBrokerCoverWithdraw::doApply() associateAsset(*broker, vaultAsset); - return doWithdraw(view(), tx, account_, dstAcct, brokerPseudoID, mPriorBalance, amount, j_); + return doWithdraw(view(), tx, account_, dstAcct, brokerPseudoID, preFeeBalance_, amount, j_); } //------------------------------------------------------------------------------ diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp index 85026654da..a755db7942 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp @@ -1,5 +1,7 @@ #include // +#include +#include #include #include diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp index c825a7fafe..f8813ddbef 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp @@ -1,5 +1,7 @@ #include // +#include +#include #include #include @@ -220,7 +222,7 @@ LoanBrokerSet::doApply() // one for the pseudo-account. adjustOwnerCount(view, owner, 2, j_); auto const ownerCount = owner->at(sfOwnerCount); - if (mPriorBalance < view.fees().accountReserve(ownerCount)) + if (preFeeBalance_ < view.fees().accountReserve(ownerCount)) return tecINSUFFICIENT_RESERVE; auto maybePseudo = createPseudoAccount(view, broker->key(), sfLoanBrokerID); @@ -229,7 +231,7 @@ LoanBrokerSet::doApply() auto& pseudo = *maybePseudo; auto pseudoId = pseudo->at(sfAccount); - if (auto ter = addEmptyHolding(view, pseudoId, mPriorBalance, sleVault->at(sfAsset), j_)) + if (auto ter = addEmptyHolding(view, pseudoId, preFeeBalance_, sleVault->at(sfAsset), j_)) return ter; // Initialize data fields: diff --git a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp index e7d219d6c8..39b28f5110 100644 --- a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp @@ -1,5 +1,6 @@ #include // +#include #include #include diff --git a/src/libxrpl/tx/transactors/lending/LoanManage.cpp b/src/libxrpl/tx/transactors/lending/LoanManage.cpp index 2dacb453b5..8c3e625963 100644 --- a/src/libxrpl/tx/transactors/lending/LoanManage.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanManage.cpp @@ -1,5 +1,6 @@ #include // +#include #include #include #include @@ -25,7 +26,7 @@ LoanManage::preflight(PreflightContext const& ctx) return temINVALID; // Flags are mutually exclusive - if (auto const flagField = ctx.tx[~sfFlags]; flagField && *flagField) + if (auto const flagField = ctx.tx[~sfFlags]; flagField && (*flagField != 0u)) { auto const flags = *flagField & tfUniversalMask; if ((flags & (flags - 1)) != 0) @@ -143,7 +144,7 @@ LoanManage::defaultLoan( TenthBips32 const coverRateLiquidation{brokerSle->at(sfCoverRateLiquidation)}; auto const defaultCovered = [&]() { // Always round the minimum required up. - NumberRoundModeGuard mg(Number::upward); + NumberRoundModeGuard const mg(Number::upward); auto const minimumCover = tenthBipsOfValue(brokerDebtTotalProxy.value(), coverRateMinimum); // Round the liquidation amount up, too auto const covered = roundToAsset( @@ -385,21 +386,29 @@ LoanManage::doApply() return tefBAD_LEDGER; // LCOV_EXCL_LINE auto const vaultAsset = vaultSle->at(sfAsset); - // Valid flag combinations are checked in preflight. No flags is valid - - // just a noop. - if (tx.isFlag(tfLoanDefault)) - return defaultLoan(view, loanSle, brokerSle, vaultSle, vaultAsset, j_); - if (tx.isFlag(tfLoanImpair)) - return impairLoan(view, loanSle, vaultSle, vaultAsset, j_); - if (tx.isFlag(tfLoanUnimpair)) - return unimpairLoan(view, loanSle, vaultSle, vaultAsset, j_); - // Noop, as described above. + auto const result = [&]() -> TER { + // Valid flag combinations are checked in preflight. No flags is valid - + // just a noop. + if (tx.isFlag(tfLoanDefault)) + return defaultLoan(view, loanSle, brokerSle, vaultSle, vaultAsset, j_); + if (tx.isFlag(tfLoanImpair)) + return impairLoan(view, loanSle, vaultSle, vaultAsset, j_); + if (tx.isFlag(tfLoanUnimpair)) + return unimpairLoan(view, loanSle, vaultSle, vaultAsset, j_); + // Noop, as described above. + return tesSUCCESS; + }(); - associateAsset(*loanSle, vaultAsset); - associateAsset(*brokerSle, vaultAsset); - associateAsset(*vaultSle, vaultAsset); + // Pre-amendment, associateAsset was only called on the noop (no flags) + // path. Post-amendment, we call associateAsset on all successful paths. + if (view.rules().enabled(fixSecurity3_1_3) && isTesSuccess(result)) + { + associateAsset(*loanSle, vaultAsset); + associateAsset(*brokerSle, vaultAsset); + associateAsset(*vaultSle, vaultAsset); + } - return tesSUCCESS; + return result; } //------------------------------------------------------------------------------ diff --git a/src/libxrpl/tx/transactors/lending/LoanPay.cpp b/src/libxrpl/tx/transactors/lending/LoanPay.cpp index d894c56631..d400fb3630 100644 --- a/src/libxrpl/tx/transactors/lending/LoanPay.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanPay.cpp @@ -1,6 +1,7 @@ #include // #include +#include #include #include #include @@ -56,9 +57,11 @@ LoanPay::calculateBaseFee(ReadView const& view, STTx const& tx) auto const normalCost = Transactor::calculateBaseFee(view, tx); if (tx.isFlag(tfLoanFullPayment) || tx.isFlag(tfLoanLatePayment)) + { // The loan will be making one set of calculations for one full or late // payment return normalCost; + } // The fee is based on the potential number of payments, unless the loan is // being fully paid off. @@ -67,8 +70,10 @@ LoanPay::calculateBaseFee(ReadView const& view, STTx const& tx) auto const loanSle = view.read(keylet::loan(loanID)); if (!loanSle) + { // Let preclaim worry about the error for this return normalCost; + } if (loanSle->at(sfPaymentRemaining) <= loanPaymentsPerFeeIncrement) { @@ -78,24 +83,32 @@ LoanPay::calculateBaseFee(ReadView const& view, STTx const& tx) } if (hasExpired(view, loanSle->at(sfNextPaymentDueDate))) + { // If the payment is late, and the late payment flag is not set, it'll // fail return normalCost; + } auto const brokerSle = view.read(keylet::loanbroker(loanSle->at(sfLoanBrokerID))); if (!brokerSle) + { // Let preclaim worry about the error for this return normalCost; + } auto const vaultSle = view.read(keylet::vault(brokerSle->at(sfVaultID))); if (!vaultSle) + { // Let preclaim worry about the error for this return normalCost; + } auto const asset = vaultSle->at(sfAsset); if (asset != amount.asset()) + { // Let preclaim worry about the error for this return normalCost; + } auto const scale = loanSle->at(sfLoanScale); @@ -104,7 +117,7 @@ LoanPay::calculateBaseFee(ReadView const& view, STTx const& tx) // If making an overpayment, count it as a full payment because it will do // about the same amount of work, if not more. - NumberRoundModeGuard mg(tx.isFlag(tfLoanOverpayment) ? Number::upward : Number::downward); + NumberRoundModeGuard const mg(tx.isFlag(tfLoanOverpayment) ? Number::upward : Number::downward); // Estimate how many payments will be made Number const numPaymentEstimate = static_cast(amount / regularPayment); @@ -142,7 +155,7 @@ LoanPay::preclaim(PreclaimContext const& ctx) if (tx.isFlag(tfLoanOverpayment) && !loanSle->isFlag(lsfLoanOverpayment)) { JLOG(ctx.j.warn()) << "Requested overpayment on a loan that doesn't allow it"; - return temINVALID_FLAG; + return ctx.view.rules().enabled(fixSecurity3_1_3) ? TER{tecNO_PERMISSION} : temINVALID_FLAG; } auto const principalOutstanding = loanSle->at(sfPrincipalOutstanding); @@ -264,7 +277,7 @@ LoanPay::doApply() // Round the minimum required cover up to be conservative. This ensures // CoverAvailable never drops below the theoretical minimum, protecting // the broker's solvency. - NumberRoundModeGuard mg(Number::upward); + NumberRoundModeGuard const mg(Number::upward); return coverAvailableProxy >= roundToAsset( asset, tenthBipsOfValue(debtTotalProxy.value(), coverRateMinimum), loanScale) && @@ -517,9 +530,11 @@ LoanPay::doApply() if (auto const ter = addEmptyHolding( view, brokerPayee, brokerPayeeSle->at(sfBalance).value().xrp(), asset, j_); ter && ter != tecDUPLICATE) + { // ignore tecDUPLICATE. That means the holding already exists, // and is fine here return ter; + } } if (auto const ter = requireAuth(view, asset, brokerPayee, AuthType::StrongAuth)) return ter; diff --git a/src/libxrpl/tx/transactors/lending/LoanSet.cpp b/src/libxrpl/tx/transactors/lending/LoanSet.cpp index f74522e737..9cc4042365 100644 --- a/src/libxrpl/tx/transactors/lending/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanSet.cpp @@ -1,5 +1,7 @@ #include // +#include +#include #include #include #include @@ -62,9 +64,10 @@ LoanSet::preflight(PreflightContext const& ctx) return temINVALID; } // Principal Requested is required - if (auto const p = tx[sfPrincipalRequested]; p <= 0) + auto const p = tx[sfPrincipalRequested]; + if (p <= 0) return temINVALID; - else if (!validNumericRange(tx[~sfLoanOriginationFee], p)) + if (!validNumericRange(tx[~sfLoanOriginationFee], p)) return temINVALID; if (!validNumericRange(tx[~sfInterestRate], maxInterestRate)) return temINVALID; @@ -80,17 +83,16 @@ LoanSet::preflight(PreflightContext const& ctx) if (auto const paymentTotal = tx[~sfPaymentTotal]; paymentTotal && *paymentTotal <= 0) return temINVALID; - if (auto const paymentInterval = tx[~sfPaymentInterval]; - !validNumericMinimum(paymentInterval, LoanSet::minPaymentInterval)) - return temINVALID; - // Grace period is between min default value and payment interval - else if ( - auto const gracePeriod = tx[~sfGracePeriod]; // - !validNumericRange( + auto const paymentInterval = tx[~sfPaymentInterval]; + if (!validNumericMinimum(paymentInterval, LoanSet::minPaymentInterval)) + return temINVALID; // Grace period is between min default value and payment interval + if (auto const gracePeriod = tx[~sfGracePeriod]; !validNumericRange( gracePeriod, paymentInterval.value_or(LoanSet::defaultPaymentInterval), defaultGracePeriod)) + { return temINVALID; + } // Copied from preflight2 if (counterPartySig) @@ -150,12 +152,12 @@ LoanSet::calculateBaseFee(ReadView const& view, STTx const& tx) // for the transaction. Note that unlike the base class, the single signer // is counted if present. It will only be absent in a batch inner // transaction. - std::size_t const signerCount = [&counterSig]() { - // Compute defensively. Assure that "tx" cannot be accessed and cause - // confusion or miscalculations. - return counterSig.isFieldPresent(sfSigners) - ? counterSig.getFieldArray(sfSigners).size() - : (counterSig.isFieldPresent(sfTxnSignature) ? 1 : 0); + std::size_t const signerCount = [&counterSig]() -> int { + // Compute defensively. + // Assure that "tx" cannot be accessed and cause confusion or miscalculations. + if (counterSig.isFieldPresent(sfSigners)) + return counterSig.getFieldArray(sfSigners).size(); + return counterSig.isFieldPresent(sfTxnSignature) ? 1 : 0; }(); return normalCost + (signerCount * baseFee); @@ -266,8 +268,10 @@ LoanSet::preclaim(PreclaimContext const& ctx) auto const vault = ctx.view.read(keylet::vault(brokerSle->at(sfVaultID))); if (!vault) + { // Should be impossible return tefBAD_LEDGER; // LCOV_EXCL_LINE + } if (vault->at(sfAssetsMaximum) != 0 && vault->at(sfAssetsTotal) >= vault->at(sfAssetsMaximum)) { @@ -462,7 +466,7 @@ LoanSet::doApply() // Round the minimum required cover up to be conservative. This ensures // CoverAvailable never drops below the theoretical minimum, protecting // the broker's solvency. - NumberRoundModeGuard mg(Number::upward); + NumberRoundModeGuard const mg(Number::upward); if (brokerSle->at(sfCoverAvailable) < tenthBipsOfValue(newDebtTotal, coverRateMinimum)) { JLOG(j_.warn()) << "Insufficient first-loss capital to cover the loan."; @@ -474,7 +478,7 @@ LoanSet::doApply() { auto const ownerCount = borrowerSle->at(sfOwnerCount); auto const balance = - account_ == borrower ? mPriorBalance : borrowerSle->at(sfBalance).value().xrp(); + account_ == borrower ? preFeeBalance_ : borrowerSle->at(sfBalance).value().xrp(); if (balance < view.fees().accountReserve(ownerCount)) return tecINSUFFICIENT_RESERVE; } @@ -492,9 +496,11 @@ LoanSet::doApply() if (auto const ter = addEmptyHolding( view, borrower, borrowerSle->at(sfBalance).value().xrp(), vaultAsset, j_); ter && ter != tecDUPLICATE) + { // ignore tecDUPLICATE. That means the holding already exists, and // is fine here return ter; + } if (auto const ter = requireAuth(view, vaultAsset, borrower, AuthType::StrongAuth)) return ter; @@ -513,9 +519,11 @@ LoanSet::doApply() if (auto const ter = addEmptyHolding( view, brokerOwner, brokerOwnerSle->at(sfBalance).value().xrp(), vaultAsset, j_); ter && ter != tecDUPLICATE) + { // ignore tecDUPLICATE. That means the holding already exists, // and is fine here return ter; + } } if (auto const ter = requireAuth(view, vaultAsset, brokerOwner, AuthType::StrongAuth)) diff --git a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp index 32af49355a..fda79c93b6 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp @@ -1,9 +1,10 @@ #include +#include +#include #include #include #include #include -#include namespace xrpl { @@ -129,12 +130,12 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { auto res = nft::checkTrustlineAuthorized( ctx.view, ctx.tx[sfAccount], ctx.j, brokerFee->asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; res = nft::checkTrustlineDeepFrozen( ctx.view, ctx.tx[sfAccount], ctx.j, brokerFee->asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; } } @@ -180,19 +181,19 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { auto res = nft::checkTrustlineAuthorized( ctx.view, bo->at(sfOwner), ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; if (!so) { res = nft::checkTrustlineAuthorized( ctx.view, ctx.tx[sfAccount], ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; res = nft::checkTrustlineDeepFrozen( ctx.view, ctx.tx[sfAccount], ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; } } @@ -248,21 +249,21 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { auto res = nft::checkTrustlineAuthorized( ctx.view, (*so)[sfOwner], ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; if (!bo) { res = nft::checkTrustlineAuthorized( ctx.view, ctx.tx[sfAccount], ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; } } auto const res = nft::checkTrustlineDeepFrozen( ctx.view, (*so)[sfOwner], ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; } } @@ -271,8 +272,10 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // this nftoken auto const& offer = bo ? bo : so; if (!offer) + { // Purely defensive, should be caught in preflight. return tecINTERNAL; // LCOV_EXCL_LINE + } auto const& tokenID = offer->at(sfNFTokenID); auto const& amount = offer->at(sfAmount); @@ -294,12 +297,12 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { auto res = nft::checkTrustlineAuthorized( ctx.view, nftMinter, ctx.j, amount.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; res = nft::checkTrustlineDeepFrozen( ctx.view, nftMinter, ctx.j, amount.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; } } @@ -321,7 +324,7 @@ NFTokenAcceptOffer::pay(AccountID const& from, AccountID const& to, STAmount con // we know that something went wrong. This was originally found in the // context of IOU transfer fees. Since there are several payouts in this tx, // just confirm that the end state is OK. - if (result != tesSUCCESS) + if (!isTesSuccess(result)) return result; if (accountFunds(view(), from, amount, fhZERO_IF_FROZEN, j_).signum() < 0) return tecINSUFFICIENT_FUNDS; @@ -341,7 +344,7 @@ NFTokenAcceptOffer::transferNFToken( if (!tokenAndPage) return tecINTERNAL; // LCOV_EXCL_LINE - if (auto const ret = nft::removeToken(view(), seller, nftokenID, std::move(tokenAndPage->page)); + if (auto const ret = nft::removeToken(view(), seller, nftokenID, tokenAndPage->page); !isTesSuccess(ret)) return ret; @@ -361,7 +364,7 @@ NFTokenAcceptOffer::transferNFToken( // NFTs free of reserve. if (view().rules().enabled(fixNFTokenReserve)) { - // To check if there is sufficient reserve, we cannot use mPriorBalance + // To check if there is sufficient reserve, we cannot use preFeeBalance_ // because NFT is sold for a price. So we must use the balance after // the deduction of the potential offer price. A small caveat here is // that the balance has already deducted the transaction fee, meaning diff --git a/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp b/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp index 6d9ee56c51..1653da3ea4 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp @@ -1,8 +1,8 @@ +#include #include #include #include #include -#include namespace xrpl { @@ -29,7 +29,7 @@ NFTokenBurn::preclaim(PreclaimContext const& ctx) // do so if the token is marked as burnable. if (auto const account = ctx.tx[sfAccount]; owner != account) { - if (!(nft::getFlags(ctx.tx[sfNFTokenID]) & nft::flagBurnable)) + if ((nft::getFlags(ctx.tx[sfNFTokenID]) & nft::flagBurnable) == 0) return tecNO_PERMISSION; if (auto const issuer = nft::getIssuer(ctx.tx[sfNFTokenID]); issuer != account) diff --git a/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp index df0561e076..699714e0ac 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp @@ -1,8 +1,8 @@ #include +#include #include #include #include -#include #include diff --git a/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp index ca90bcafab..19bf34c560 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp @@ -1,8 +1,8 @@ #include +#include #include #include #include -#include namespace xrpl { @@ -42,10 +42,10 @@ NFTokenCreateOffer::preclaim(PreclaimContext const& ctx) return tecEXPIRED; uint256 const nftokenID = ctx.tx[sfNFTokenID]; - std::uint32_t const txFlags = {ctx.tx.getFlags()}; + std::uint32_t const txFlags = ctx.tx.getFlags(); if (!nft::findToken( - ctx.view, ctx.tx[(txFlags & tfSellNFToken) ? sfAccount : sfOwner], nftokenID)) + ctx.view, ctx.tx[((txFlags & tfSellNFToken) != 0u) ? sfAccount : sfOwner], nftokenID)) return tecNO_ENTRY; // Use implementation shared with NFTokenMint @@ -74,7 +74,7 @@ NFTokenCreateOffer::doApply() ctx_.tx[~sfExpiration], ctx_.tx.getSeqProxy(), ctx_.tx[sfNFTokenID], - mPriorBalance, + preFeeBalance_, j_, ctx_.tx.getFlags()); } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp index a643a6042c..7bebbd0070 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp @@ -47,11 +47,16 @@ NFTokenMint::getFlagsMask(PreflightContext const& ctx) // The fixRemoveNFTokenAutoTrustLine amendment disables minting with the // tfTrustLine flag as a way to prevent the attack. But until the // amendment passes we still need to keep the old behavior available. - std::uint32_t const nfTokenMintMask = ctx.rules.enabled(fixRemoveNFTokenAutoTrustLine) - // if featureDynamicNFT enabled then new flag allowing mutable URI available - ? ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintMask : tfNFTokenMintMaskWithoutMutable - : ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintOldMaskWithMutable - : tfNFTokenMintOldMask; + std::uint32_t const nfTokenMintMask = [&]() -> std::uint32_t { + if (ctx.rules.enabled(fixRemoveNFTokenAutoTrustLine)) + { + // if featureDynamicNFT enabled then new flag allowing mutable URI available + return ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintMask + : tfNFTokenMintMaskWithoutMutable; + } + return ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintOldMaskWithMutable + : tfNFTokenMintOldMask; + }(); return nfTokenMintMask; } @@ -76,7 +81,7 @@ NFTokenMint::preflight(PreflightContext const& ctx) if (auto uri = ctx.tx[~sfURI]) { - if (uri->length() == 0 || uri->length() > maxTokenURILength) + if (uri->empty() || uri->length() > maxTokenURILength) return temMALFORMED; } @@ -202,8 +207,10 @@ NFTokenMint::doApply() auto const tokenSeq = [this, &issuer]() -> Expected { auto const root = view().peek(keylet::account(issuer)); if (root == nullptr) + { // Should not happen. Checked in preclaim. return Unexpected(tecNO_ISSUER); + } // If the issuer hasn't minted an NFToken before we must add a // FirstNFTokenSequence field to the issuer's AccountRoot. The @@ -259,8 +266,10 @@ NFTokenMint::doApply() InnerObjectFormats::getInstance().findSOTemplateBySField(sfNFToken); if (nfTokenTemplate == nullptr) + { // Should never happen. return tecINTERNAL; // LCOV_EXCL_LINE + } auto const nftokenID = createNFTokenID( extractNFTokenFlagsFromTxFlags(ctx_.tx.getFlags()), @@ -277,7 +286,7 @@ NFTokenMint::doApply() }); if (TER const ret = nft::insertToken(ctx_.view(), account_, std::move(newToken)); - ret != tesSUCCESS) + !isTesSuccess(ret)) return ret; if (ctx_.tx.isFieldPresent(sfAmount)) @@ -293,7 +302,7 @@ NFTokenMint::doApply() ctx_.tx[~sfExpiration], ctx_.tx.getSeqProxy(), nftokenID, - mPriorBalance, + preFeeBalance_, j_); !isTesSuccess(ter)) return ter; @@ -308,7 +317,7 @@ NFTokenMint::doApply() ownerCountAfter > ownerCountBefore) { if (auto const reserve = view().fees().accountReserve(ownerCountAfter); - mPriorBalance < reserve) + preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; } return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp b/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp index af852530e4..79c019de96 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp @@ -1,7 +1,7 @@ +#include #include #include #include -#include namespace xrpl { @@ -13,7 +13,7 @@ NFTokenModify::preflight(PreflightContext const& ctx) if (auto uri = ctx.tx[~sfURI]) { - if (uri->length() == 0 || uri->length() > maxTokenURILength) + if (uri->empty() || uri->length() > maxTokenURILength) return temMALFORMED; } @@ -30,7 +30,7 @@ NFTokenModify::preclaim(PreclaimContext const& ctx) return tecNO_ENTRY; // Check if the NFT is mutable - if (!(nft::getFlags(ctx.tx[sfNFTokenID]) & nft::flagMutable)) + if ((nft::getFlags(ctx.tx[sfNFTokenID]) & nft::flagMutable) == 0) return tecNO_PERMISSION; // Verify permissions for the issuer diff --git a/src/libxrpl/tx/transactors/oracle/DeleteOracle.cpp b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp similarity index 77% rename from src/libxrpl/tx/transactors/oracle/DeleteOracle.cpp rename to src/libxrpl/tx/transactors/oracle/OracleDelete.cpp index 5452096b6d..bde403f821 100644 --- a/src/libxrpl/tx/transactors/oracle/DeleteOracle.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp @@ -1,31 +1,33 @@ #include +#include #include #include #include -#include +#include namespace xrpl { NotTEC -DeleteOracle::preflight(PreflightContext const& ctx) +OracleDelete::preflight(PreflightContext const& ctx) { return tesSUCCESS; } TER -DeleteOracle::preclaim(PreclaimContext const& ctx) +OracleDelete::preclaim(PreclaimContext const& ctx) { if (!ctx.view.exists(keylet::account(ctx.tx.getAccountID(sfAccount)))) return terNO_ACCOUNT; // LCOV_EXCL_LINE - if (auto const sle = ctx.view.read( - keylet::oracle(ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID])); - !sle) + auto const sle = + ctx.view.read(keylet::oracle(ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID])); + if (!sle) { JLOG(ctx.j.debug()) << "Oracle Delete: Oracle does not exist."; return tecNO_ENTRY; } - else if (ctx.tx.getAccountID(sfAccount) != sle->getAccountID(sfOwner)) + + if (ctx.tx.getAccountID(sfAccount) != sle->getAccountID(sfOwner)) { // this can't happen because of the above check // LCOV_EXCL_START @@ -37,7 +39,7 @@ DeleteOracle::preclaim(PreclaimContext const& ctx) } TER -DeleteOracle::deleteOracle( +OracleDelete::deleteOracle( ApplyView& view, std::shared_ptr const& sle, AccountID const& account, @@ -68,7 +70,7 @@ DeleteOracle::deleteOracle( } TER -DeleteOracle::doApply() +OracleDelete::doApply() { if (auto sle = ctx_.view().peek(keylet::oracle(account_, ctx_.tx[sfOracleDocumentID]))) return deleteOracle(ctx_.view(), sle, account_, j_); diff --git a/src/libxrpl/tx/transactors/oracle/SetOracle.cpp b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp similarity index 95% rename from src/libxrpl/tx/transactors/oracle/SetOracle.cpp rename to src/libxrpl/tx/transactors/oracle/OracleSet.cpp index be827c25d5..772756ad6d 100644 --- a/src/libxrpl/tx/transactors/oracle/SetOracle.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp @@ -1,10 +1,12 @@ #include #include +#include +#include #include #include #include #include -#include +#include namespace xrpl { @@ -17,7 +19,7 @@ tokenPairKey(STObject const& pair) } NotTEC -SetOracle::preflight(PreflightContext const& ctx) +OracleSet::preflight(PreflightContext const& ctx) { auto const& dataSeries = ctx.tx.getFieldArray(sfPriceDataSeries); if (dataSeries.empty()) @@ -38,7 +40,7 @@ SetOracle::preflight(PreflightContext const& ctx) } TER -SetOracle::preclaim(PreclaimContext const& ctx) +OracleSet::preclaim(PreclaimContext const& ctx) { auto const sleSetter = ctx.view.read(keylet::account(ctx.tx.getAccountID(sfAccount))); if (!sleSetter) @@ -77,11 +79,17 @@ SetOracle::preclaim(PreclaimContext const& ctx) if (entry[~sfScale] > maxPriceScale) return temMALFORMED; if (entry.isFieldPresent(sfAssetPrice)) + { pairs.emplace(key); + } else if (sle) + { pairsDel.emplace(key); + } else + { return temMALFORMED; + } } // Lambda is used to check if the value of a field, passed @@ -92,7 +100,7 @@ SetOracle::preclaim(PreclaimContext const& ctx) return !v || *v == (*sle)[field]; }; - std::uint32_t adjustReserve = 0; + std::int8_t adjustReserve = 0; if (sle) { // update @@ -111,9 +119,13 @@ SetOracle::preclaim(PreclaimContext const& ctx) if (!pairs.contains(key)) { if (pairsDel.contains(key)) + { pairsDel.erase(key); + } else + { pairs.emplace(key); + } } } if (!pairsDel.empty()) @@ -168,7 +180,7 @@ setPriceDataInnerObjTemplate(STObject& obj) } TER -SetOracle::doApply() +OracleSet::doApply() { auto const oracleID = keylet::oracle(account_, ctx_.tx[sfOracleDocumentID]); @@ -224,7 +236,7 @@ SetOracle::doApply() } STArray updatedSeries; for (auto const& iter : pairs) - updatedSeries.push_back(std::move(iter.second)); + updatedSeries.push_back(iter.second); sle->setFieldArray(sfPriceDataSeries, updatedSeries); if (ctx_.tx.isFieldPresent(sfURI)) sle->setFieldVL(sfURI, ctx_.tx[sfURI]); @@ -272,7 +284,7 @@ SetOracle::doApply() pairs.emplace(key, std::move(priceData)); } for (auto const& iter : pairs) - series.push_back(std::move(iter.second)); + series.push_back(iter.second); } sle->setFieldArray(sfPriceDataSeries, series); diff --git a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp index cd86a6906c..0f4681a3ad 100644 --- a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp +++ b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp @@ -1,6 +1,8 @@ #include -#include #include +#include +#include +#include #include #include #include @@ -17,10 +19,7 @@ DepositPreauth::checkExtraFeatures(PreflightContext const& ctx) bool const unauthArrPresent = ctx.tx.isFieldPresent(sfUnauthorizeCredentials); bool const authCredPresent = authArrPresent || unauthArrPresent; - if (authCredPresent && !ctx.rules.enabled(featureCredentials)) - return false; - - return true; + return !authCredPresent || ctx.rules.enabled(featureCredentials); } NotTEC @@ -44,7 +43,7 @@ DepositPreauth::preflight(PreflightContext const& ctx) return temMALFORMED; } - if (authPresent) + if (authPresent != 0) { // Make sure that the passed account is valid. AccountID const& target(optAuth ? *optAuth : *optUnauth); @@ -147,7 +146,7 @@ DepositPreauth::doApply() STAmount const reserve{ view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; - if (mPriorBalance < reserve) + if (preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; } @@ -194,7 +193,7 @@ DepositPreauth::doApply() STAmount const reserve{ view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; - if (mPriorBalance < reserve) + if (preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; } @@ -249,7 +248,7 @@ DepositPreauth::doApply() TER DepositPreauth::removeFromLedger(ApplyView& view, uint256 const& preauthIndex, beast::Journal j) { - // Existence already checked in preclaim and DeleteAccount + // Existence already checked in preclaim and AccountDelete auto const slePreauth{view.peek(keylet::depositPreauth(preauthIndex))}; if (!slePreauth) { diff --git a/src/libxrpl/tx/transactors/payment/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp index 1605c7d886..e3ceddeae3 100644 --- a/src/libxrpl/tx/transactors/payment/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -1,13 +1,16 @@ -#include -#include #include +#include +#include +#include +#include +#include +#include #include #include +#include #include #include #include -#include -#include #include namespace xrpl { @@ -33,15 +36,19 @@ getMaxSourceAmount( std::optional const& sendMax) { if (sendMax) + { return *sendMax; - else if (dstAmount.native() || dstAmount.holds()) + } + if (dstAmount.native() || dstAmount.holds()) + { return dstAmount; - else - return STAmount( - Issue{dstAmount.get().currency, account}, - dstAmount.mantissa(), - dstAmount.exponent(), - dstAmount < beast::zero); + } + + return STAmount( + Issue{dstAmount.get().currency, account}, + dstAmount.mantissa(), + dstAmount.exponent(), + dstAmount < beast::zero); } bool @@ -83,9 +90,9 @@ Payment::preflight(PreflightContext const& ctx) if (mptDirect && ctx.tx.isFieldPresent(sfPaths)) return temMALFORMED; - bool const partialPaymentAllowed = txFlags & tfPartialPayment; - bool const limitQuality = txFlags & tfLimitQuality; - bool const defaultPathsAllowed = !(txFlags & tfNoRippleDirect); + bool const partialPaymentAllowed = (txFlags & tfPartialPayment) != 0u; + bool const limitQuality = (txFlags & tfLimitQuality) != 0u; + bool const defaultPathsAllowed = (txFlags & tfNoRippleDirect) == 0u; bool const hasPaths = tx.isFieldPresent(sfPaths); bool const hasMax = tx.isFieldPresent(sfSendMax); @@ -231,7 +238,7 @@ Payment::checkPermission(ReadView const& view, STTx const& tx) if (!sle) return terNO_DELEGATE_PERMISSION; - if (checkTxPermission(sle, tx) == tesSUCCESS) + if (isTesSuccess(checkTxPermission(sle, tx))) return tesSUCCESS; std::unordered_set granularPermissions; @@ -261,7 +268,7 @@ Payment::preclaim(PreclaimContext const& ctx) { // Ripple if source or destination is non-native or if there are paths. std::uint32_t const txFlags = ctx.tx.getFlags(); - bool const partialPaymentAllowed = txFlags & tfPartialPayment; + bool const partialPaymentAllowed = (txFlags & tfPartialPayment) != 0u; auto const hasPaths = ctx.tx.isFieldPresent(sfPaths); auto const sendMax = ctx.tx[~sfSendMax]; @@ -282,7 +289,7 @@ Payment::preclaim(PreclaimContext const& ctx) // transaction would succeed. return tecNO_DST; } - else if (ctx.view.open() && partialPaymentAllowed) + if (ctx.view.open() && partialPaymentAllowed) { // You cannot fund an account with a partial payment. // Make retry work smaller, by rejecting this. @@ -293,7 +300,7 @@ Payment::preclaim(PreclaimContext const& ctx) // transaction would succeed. return telNO_DST_PARTIAL; } - else if (dstAmount < STAmount(ctx.view.fees().reserve)) + if (dstAmount < STAmount(ctx.view.fees().reserve)) { // accountReserve is the minimum amount that an account can have. // Reserve is not scaled by load. @@ -306,7 +313,9 @@ Payment::preclaim(PreclaimContext const& ctx) return tecNO_DST_INSUF_XRP; } } - else if ((sleDst->getFlags() & lsfRequireDestTag) && !ctx.tx.isFieldPresent(sfDestinationTag)) + else if ( + ((sleDst->getFlags() & lsfRequireDestTag) != 0u) && + !ctx.tx.isFieldPresent(sfDestinationTag)) { // The tag is basically account-specific information we don't // understand, but we can require someone to fill it in. @@ -355,9 +364,9 @@ Payment::doApply() // Ripple if source or destination is non-native or if there are paths. std::uint32_t const txFlags = ctx_.tx.getFlags(); - bool const partialPaymentAllowed = txFlags & tfPartialPayment; - bool const limitQuality = txFlags & tfLimitQuality; - bool const defaultPathsAllowed = !(txFlags & tfNoRippleDirect); + bool const partialPaymentAllowed = (txFlags & tfPartialPayment) != 0u; + bool const limitQuality = (txFlags & tfLimitQuality) != 0u; + bool const defaultPathsAllowed = (txFlags & tfNoRippleDirect) == 0u; auto const hasPaths = ctx_.tx.isFieldPresent(sfPaths); auto const sendMax = ctx_.tx[~sfSendMax]; @@ -394,7 +403,7 @@ Payment::doApply() if (ripple) { - // Ripple payment with at least one intermediate step and uses + // XRPL payment with at least one intermediate step and uses // transitive balances. // An account that requires authorization has two ways to get an @@ -425,7 +434,7 @@ Payment::doApply() account_, ctx_.tx.getFieldPathSet(sfPaths), ctx_.tx[~sfDomainID], - ctx_.registry.logs(), + ctx_.registry, &rcInput); // VFALCO NOTE We might not need to apply, depending // on the TER. But always applying *should* @@ -435,12 +444,16 @@ Payment::doApply() // TODO: is this right? If the amount is the correct amount, was // the delivered amount previously set? - if (rc.result() == tesSUCCESS && rc.actualAmountOut != dstAmount) + if (isTesSuccess(rc.result()) && rc.actualAmountOut != dstAmount) { if (deliverMin && rc.actualAmountOut < *deliverMin) + { rc.setResult(tecPATH_PARTIAL); + } else + { ctx_.deliver(rc.actualAmountOut); + } } auto terResult = rc.result(); @@ -453,19 +466,19 @@ Payment::doApply() terResult = tecPATH_DRY; return terResult; } - else if (mptDirect) + if (mptDirect) { JLOG(j_.trace()) << " dstAmount=" << dstAmount.getFullText(); auto const& mptIssue = dstAmount.get(); - if (auto const ter = requireAuth(view(), mptIssue, account_); ter != tesSUCCESS) + if (auto const ter = requireAuth(view(), mptIssue, account_); !isTesSuccess(ter)) return ter; - if (auto const ter = requireAuth(view(), mptIssue, dstAccountID); ter != tesSUCCESS) + if (auto const ter = requireAuth(view(), mptIssue, dstAccountID); !isTesSuccess(ter)) return ter; if (auto const ter = canTransfer(view(), mptIssue, account_, dstAccountID); - ter != tesSUCCESS) + !isTesSuccess(ter)) return ter; if (auto err = verifyDepositPreauth( @@ -513,7 +526,7 @@ Payment::doApply() PaymentSandbox pv(&view()); auto res = accountSend(pv, account_, dstAccountID, amountDeliver, ctx_.journal); - if (res == tesSUCCESS) + if (isTesSuccess(res)) { pv.apply(ctx_.rawView()); @@ -524,7 +537,9 @@ Payment::doApply() ctx_.deliver(amountDeliver); } else if (res == tecINSUFFICIENT_FUNDS || res == tecPATH_DRY) + { res = tecPATH_PARTIAL; + } return res; } @@ -544,18 +559,23 @@ Payment::doApply() // This is the total reserve in drops. auto const reserve = view().fees().accountReserve(ownerCount); - // mPriorBalance is the balance on the sending account BEFORE the - // fees were charged. We want to make sure we have enough reserve - // to send. Allow final spend to use reserve for fee. - auto const mmm = std::max(reserve, ctx_.tx.getFieldAmount(sfFee).xrp()); + // In a delegated payment, the fee payer is the delegated account, + // not the source account (account_). + bool const accountIsPayer = (ctx_.tx.getFeePayer() == account_); - if (mPriorBalance < dstAmount.xrp() + mmm) + // preFeeBalance_ is the balance on the source account (account_) BEFORE the fees + // were charged. If source account is the fee payer, it must also cover the fee. + // The final spend may use the reserve to cover fees. + auto const minRequiredFunds = + accountIsPayer ? std::max(reserve, ctx_.tx.getFieldAmount(sfFee).xrp()) : reserve; + + if (preFeeBalance_ < dstAmount.xrp() + minRequiredFunds) { // Vote no. However the transaction might succeed, if applied in // a different order. - JLOG(j_.trace()) << "Delay transaction: Insufficient funds: " << to_string(mPriorBalance) - << " / " << to_string(dstAmount.xrp() + mmm) << " (" << to_string(reserve) - << ")"; + JLOG(j_.trace()) << "Delay transaction: Insufficient funds: " << to_string(preFeeBalance_) + << " / " << to_string(dstAmount.xrp() + minRequiredFunds) << " (" + << to_string(reserve) << ")"; return tecUNFUNDED_PAYMENT; } @@ -601,11 +621,11 @@ Payment::doApply() } // Do the arithmetic for the transfer and make the ledger change. - sleSrc->setFieldAmount(sfBalance, mSourceBalance - dstAmount); + sleSrc->setFieldAmount(sfBalance, sleSrc->getFieldAmount(sfBalance) - dstAmount); sleDst->setFieldAmount(sfBalance, sleDst->getFieldAmount(sfBalance) + dstAmount); // Re-arm the password change fee if we can and need to. - if ((sleDst->getFlags() & lsfPasswordSpent)) + if ((sleDst->getFlags() & lsfPasswordSpent) != 0u) sleDst->clearFlag(lsfPasswordSpent); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/payment_channel/PayChan.cpp b/src/libxrpl/tx/transactors/payment_channel/PayChan.cpp deleted file mode 100644 index f963153e87..0000000000 --- a/src/libxrpl/tx/transactors/payment_channel/PayChan.cpp +++ /dev/null @@ -1,542 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace xrpl { - -/* - PaymentChannel - - Payment channels permit off-ledger checkpoints of XRP payments flowing - in a single direction. A channel sequesters the owner's XRP in its own - ledger entry. The owner can authorize the recipient to claim up to a - given balance by giving the receiver a signed message (off-ledger). The - recipient can use this signed message to claim any unpaid balance while - the channel remains open. The owner can top off the line as needed. If - the channel has not paid out all its funds, the owner must wait out a - delay to close the channel to give the recipient a chance to supply any - claims. The recipient can close the channel at any time. Any transaction - that touches the channel after the expiration time will close the - channel. The total amount paid increases monotonically as newer claims - are issued. When the channel is closed any remaining balance is returned - to the owner. Channels are intended to permit intermittent off-ledger - settlement of ILP trust lines as balances get substantial. For - bidirectional channels, a payment channel can be used in each direction. - - PaymentChannelCreate - - Create a unidirectional channel. The parameters are: - Destination - The recipient at the end of the channel. - Amount - The amount of XRP to deposit in the channel immediately. - SettleDelay - The amount of time everyone but the recipient must wait for a - superior claim. - PublicKey - The key that will sign claims against the channel. - CancelAfter (optional) - Any channel transaction that touches this channel after the - `CancelAfter` time will close it. - DestinationTag (optional) - Destination tags allow the different accounts inside of a Hosted - Wallet to be mapped back onto the Ripple ledger. The destination tag - tells the server to which account in the Hosted Wallet the funds are - intended to go to. Required if the destination has lsfRequireDestTag - set. - SourceTag (optional) - Source tags allow the different accounts inside of a Hosted Wallet - to be mapped back onto the Ripple ledger. Source tags are similar to - destination tags but are for the channel owner to identify their own - transactions. - - PaymentChannelFund - - Add additional funds to the payment channel. Only the channel owner may - use this transaction. The parameters are: - Channel - The 256-bit ID of the channel. - Amount - The amount of XRP to add. - Expiration (optional) - Time the channel closes. The transaction will fail if the expiration - times does not satisfy the SettleDelay constraints. - - PaymentChannelClaim - - Place a claim against an existing channel. The parameters are: - Channel - The 256-bit ID of the channel. - Balance (optional) - The total amount of XRP delivered after this claim is processed - (optional, not needed if just closing). Amount (optional) The amount of XRP - the signature is for (not needed if equal to Balance or just closing the - line). Signature (optional) Authorization for the balance above, signed by - the owner (optional, not needed if closing or owner is performing the - transaction). The signature if for the following message: CLM\0 followed by - the 256-bit channel ID, and a 64-bit integer drops. PublicKey (optional) The - public key that made the signature (optional, required if a signature is - present) Flags tfClose Request that the channel be closed tfRenew Request - that the channel's expiration be reset. Only the owner may renew a channel. - -*/ - -//------------------------------------------------------------------------------ - -static TER -closeChannel( - std::shared_ptr const& slep, - ApplyView& view, - uint256 const& key, - beast::Journal j) -{ - AccountID const src = (*slep)[sfAccount]; - // Remove PayChan from owner directory - { - auto const page = (*slep)[sfOwnerNode]; - if (!view.dirRemove(keylet::ownerDir(src), page, key, true)) - { - // LCOV_EXCL_START - JLOG(j.fatal()) << "Could not remove paychan from src owner directory"; - return tefBAD_LEDGER; - // LCOV_EXCL_STOP - } - } - - // Remove PayChan from recipient's owner directory, if present. - if (auto const page = (*slep)[~sfDestinationNode]) - { - auto const dst = (*slep)[sfDestination]; - if (!view.dirRemove(keylet::ownerDir(dst), *page, key, true)) - { - // LCOV_EXCL_START - JLOG(j.fatal()) << "Could not remove paychan from dst owner directory"; - return tefBAD_LEDGER; - // LCOV_EXCL_STOP - } - } - - // Transfer amount back to owner, decrement owner count - auto const sle = view.peek(keylet::account(src)); - if (!sle) - return tefINTERNAL; // LCOV_EXCL_LINE - - XRPL_ASSERT( - (*slep)[sfAmount] >= (*slep)[sfBalance], "xrpl::closeChannel : minimum channel amount"); - (*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance]; - adjustOwnerCount(view, sle, -1, j); - view.update(sle); - - // Remove PayChan from ledger - view.erase(slep); - return tesSUCCESS; -} - -//------------------------------------------------------------------------------ - -TxConsequences -PayChanCreate::makeTxConsequences(PreflightContext const& ctx) -{ - return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()}; -} - -NotTEC -PayChanCreate::preflight(PreflightContext const& ctx) -{ - if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::zero)) - return temBAD_AMOUNT; - - if (ctx.tx[sfAccount] == ctx.tx[sfDestination]) - return temDST_IS_SRC; - - if (!publicKeyType(ctx.tx[sfPublicKey])) - return temMALFORMED; - - return tesSUCCESS; -} - -TER -PayChanCreate::preclaim(PreclaimContext const& ctx) -{ - auto const account = ctx.tx[sfAccount]; - auto const sle = ctx.view.read(keylet::account(account)); - if (!sle) - return terNO_ACCOUNT; - - // Check reserve and funds availability - { - auto const balance = (*sle)[sfBalance]; - auto const reserve = ctx.view.fees().accountReserve((*sle)[sfOwnerCount] + 1); - - if (balance < reserve) - return tecINSUFFICIENT_RESERVE; - - if (balance < reserve + ctx.tx[sfAmount]) - return tecUNFUNDED; - } - - auto const dst = ctx.tx[sfDestination]; - - { - // Check destination account - auto const sled = ctx.view.read(keylet::account(dst)); - if (!sled) - return tecNO_DST; - - auto const flags = sled->getFlags(); - - // Check if they have disallowed incoming payment channels - if (flags & lsfDisallowIncomingPayChan) - return tecNO_PERMISSION; - - if ((flags & lsfRequireDestTag) && !ctx.tx[~sfDestinationTag]) - return tecDST_TAG_NEEDED; - - // Pseudo-accounts cannot receive payment channels, other than native - // to their underlying ledger object - implemented in their respective - // transaction types. Note, this is not amendment-gated because all - // writes to pseudo-account discriminator fields **are** amendment - // gated, hence the behaviour of this check will always match the - // currently active amendments. - if (isPseudoAccount(sled)) - return tecNO_PERMISSION; - } - - return tesSUCCESS; -} - -TER -PayChanCreate::doApply() -{ - auto const account = ctx_.tx[sfAccount]; - auto const sle = ctx_.view().peek(keylet::account(account)); - if (!sle) - return tefINTERNAL; // LCOV_EXCL_LINE - - if (ctx_.view().rules().enabled(fixPayChanCancelAfter)) - { - auto const closeTime = ctx_.view().header().parentCloseTime; - if (ctx_.tx[~sfCancelAfter] && after(closeTime, ctx_.tx[sfCancelAfter])) - return tecEXPIRED; - } - - auto const dst = ctx_.tx[sfDestination]; - - // Create PayChan in ledger. - // - // Note that we we use the value from the sequence or ticket as the - // payChan sequence. For more explanation see comments in SeqProxy.h. - Keylet const payChanKeylet = keylet::payChan(account, dst, ctx_.tx.getSeqValue()); - auto const slep = std::make_shared(payChanKeylet); - - // Funds held in this channel - (*slep)[sfAmount] = ctx_.tx[sfAmount]; - // Amount channel has already paid - (*slep)[sfBalance] = ctx_.tx[sfAmount].zeroed(); - (*slep)[sfAccount] = account; - (*slep)[sfDestination] = dst; - (*slep)[sfSettleDelay] = ctx_.tx[sfSettleDelay]; - (*slep)[sfPublicKey] = ctx_.tx[sfPublicKey]; - (*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter]; - (*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag]; - (*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag]; - if (ctx_.view().rules().enabled(fixIncludeKeyletFields)) - { - (*slep)[sfSequence] = ctx_.tx.getSeqValue(); - } - - ctx_.view().insert(slep); - - // Add PayChan to owner directory - { - auto const page = ctx_.view().dirInsert( - keylet::ownerDir(account), payChanKeylet, describeOwnerDir(account)); - if (!page) - return tecDIR_FULL; // LCOV_EXCL_LINE - (*slep)[sfOwnerNode] = *page; - } - - // Add PayChan to the recipient's owner directory - { - auto const page = - ctx_.view().dirInsert(keylet::ownerDir(dst), payChanKeylet, describeOwnerDir(dst)); - if (!page) - return tecDIR_FULL; // LCOV_EXCL_LINE - (*slep)[sfDestinationNode] = *page; - } - - // Deduct owner's balance, increment owner count - (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; - adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); - ctx_.view().update(sle); - - return tesSUCCESS; -} - -//------------------------------------------------------------------------------ - -TxConsequences -PayChanFund::makeTxConsequences(PreflightContext const& ctx) -{ - return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()}; -} - -NotTEC -PayChanFund::preflight(PreflightContext const& ctx) -{ - if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::zero)) - return temBAD_AMOUNT; - - return tesSUCCESS; -} - -TER -PayChanFund::doApply() -{ - Keylet const k(ltPAYCHAN, ctx_.tx[sfChannel]); - auto const slep = ctx_.view().peek(k); - if (!slep) - return tecNO_ENTRY; - - AccountID const src = (*slep)[sfAccount]; - auto const txAccount = ctx_.tx[sfAccount]; - auto const expiration = (*slep)[~sfExpiration]; - - { - auto const cancelAfter = (*slep)[~sfCancelAfter]; - auto const closeTime = ctx_.view().header().parentCloseTime.time_since_epoch().count(); - if ((cancelAfter && closeTime >= *cancelAfter) || (expiration && closeTime >= *expiration)) - return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.journal("View")); - } - - if (src != txAccount) - // only the owner can add funds or extend - return tecNO_PERMISSION; - - if (auto extend = ctx_.tx[~sfExpiration]) - { - auto minExpiration = ctx_.view().header().parentCloseTime.time_since_epoch().count() + - (*slep)[sfSettleDelay]; - if (expiration && *expiration < minExpiration) - minExpiration = *expiration; - - if (*extend < minExpiration) - return temBAD_EXPIRATION; - (*slep)[~sfExpiration] = *extend; - ctx_.view().update(slep); - } - - auto const sle = ctx_.view().peek(keylet::account(txAccount)); - if (!sle) - return tefINTERNAL; // LCOV_EXCL_LINE - - { - // Check reserve and funds availability - auto const balance = (*sle)[sfBalance]; - auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount]); - - if (balance < reserve) - return tecINSUFFICIENT_RESERVE; - - if (balance < reserve + ctx_.tx[sfAmount]) - return tecUNFUNDED; - } - - // do not allow adding funds if dst does not exist - if (AccountID const dst = (*slep)[sfDestination]; !ctx_.view().read(keylet::account(dst))) - { - return tecNO_DST; - } - - (*slep)[sfAmount] = (*slep)[sfAmount] + ctx_.tx[sfAmount]; - ctx_.view().update(slep); - - (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; - ctx_.view().update(sle); - - return tesSUCCESS; -} - -//------------------------------------------------------------------------------ - -bool -PayChanClaim::checkExtraFeatures(PreflightContext const& ctx) -{ - return !ctx.tx.isFieldPresent(sfCredentialIDs) || ctx.rules.enabled(featureCredentials); -} - -std::uint32_t -PayChanClaim::getFlagsMask(PreflightContext const&) -{ - return tfPaymentChannelClaimMask; -} - -NotTEC -PayChanClaim::preflight(PreflightContext const& ctx) -{ - auto const bal = ctx.tx[~sfBalance]; - if (bal && (!isXRP(*bal) || *bal <= beast::zero)) - return temBAD_AMOUNT; - - auto const amt = ctx.tx[~sfAmount]; - if (amt && (!isXRP(*amt) || *amt <= beast::zero)) - return temBAD_AMOUNT; - - if (bal && amt && *bal > *amt) - return temBAD_AMOUNT; - - { - auto const flags = ctx.tx.getFlags(); - - if ((flags & tfClose) && (flags & tfRenew)) - return temMALFORMED; - } - - if (auto const sig = ctx.tx[~sfSignature]) - { - if (!(ctx.tx[~sfPublicKey] && bal)) - return temMALFORMED; - - // Check the signature - // The signature isn't needed if txAccount == src, but if it's - // present, check it - - auto const reqBalance = bal->xrp(); - auto const authAmt = amt ? amt->xrp() : reqBalance; - - if (reqBalance > authAmt) - return temBAD_AMOUNT; - - Keylet const k(ltPAYCHAN, ctx.tx[sfChannel]); - if (!publicKeyType(ctx.tx[sfPublicKey])) - return temMALFORMED; - - PublicKey const pk(ctx.tx[sfPublicKey]); - Serializer msg; - serializePayChanAuthorization(msg, k.key, authAmt); - if (!verify(pk, msg.slice(), *sig)) - return temBAD_SIGNATURE; - } - - if (auto const err = credentials::checkFields(ctx.tx, ctx.j); !isTesSuccess(err)) - return err; - - return tesSUCCESS; -} - -TER -PayChanClaim::preclaim(PreclaimContext const& ctx) -{ - if (!ctx.view.rules().enabled(featureCredentials)) - return Transactor::preclaim(ctx); - - if (auto const err = credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j); - !isTesSuccess(err)) - return err; - - return tesSUCCESS; -} - -TER -PayChanClaim::doApply() -{ - Keylet const k(ltPAYCHAN, ctx_.tx[sfChannel]); - auto const slep = ctx_.view().peek(k); - if (!slep) - return tecNO_TARGET; - - AccountID const src = (*slep)[sfAccount]; - AccountID const dst = (*slep)[sfDestination]; - AccountID const txAccount = ctx_.tx[sfAccount]; - - auto const curExpiration = (*slep)[~sfExpiration]; - { - auto const cancelAfter = (*slep)[~sfCancelAfter]; - auto const closeTime = ctx_.view().header().parentCloseTime.time_since_epoch().count(); - if ((cancelAfter && closeTime >= *cancelAfter) || - (curExpiration && closeTime >= *curExpiration)) - return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.journal("View")); - } - - if (txAccount != src && txAccount != dst) - return tecNO_PERMISSION; - - if (ctx_.tx[~sfBalance]) - { - auto const chanBalance = slep->getFieldAmount(sfBalance).xrp(); - auto const chanFunds = slep->getFieldAmount(sfAmount).xrp(); - auto const reqBalance = ctx_.tx[sfBalance].xrp(); - - if (txAccount == dst && !ctx_.tx[~sfSignature]) - return temBAD_SIGNATURE; - - if (ctx_.tx[~sfSignature]) - { - PublicKey const pk((*slep)[sfPublicKey]); - if (ctx_.tx[sfPublicKey] != pk) - return temBAD_SIGNER; - } - - if (reqBalance > chanFunds) - return tecUNFUNDED_PAYMENT; - - if (reqBalance <= chanBalance) - // nothing requested - return tecUNFUNDED_PAYMENT; - - auto const sled = ctx_.view().peek(keylet::account(dst)); - if (!sled) - return tecNO_DST; - - if (auto err = - verifyDepositPreauth(ctx_.tx, ctx_.view(), txAccount, dst, sled, ctx_.journal); - !isTesSuccess(err)) - return err; - - (*slep)[sfBalance] = ctx_.tx[sfBalance]; - XRPAmount const reqDelta = reqBalance - chanBalance; - XRPL_ASSERT(reqDelta >= beast::zero, "xrpl::PayChanClaim::doApply : minimum balance delta"); - (*sled)[sfBalance] = (*sled)[sfBalance] + reqDelta; - ctx_.view().update(sled); - ctx_.view().update(slep); - } - - if (ctx_.tx.getFlags() & tfRenew) - { - if (src != txAccount) - return tecNO_PERMISSION; - (*slep)[~sfExpiration] = std::nullopt; - ctx_.view().update(slep); - } - - if (ctx_.tx.getFlags() & tfClose) - { - // Channel will close immediately if dry or the receiver closes - if (dst == txAccount || (*slep)[sfBalance] == (*slep)[sfAmount]) - return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.journal("View")); - - auto const settleExpiration = - ctx_.view().header().parentCloseTime.time_since_epoch().count() + - (*slep)[sfSettleDelay]; - - if (!curExpiration || *curExpiration > settleExpiration) - { - (*slep)[~sfExpiration] = settleExpiration; - ctx_.view().update(slep); - } - } - - return tesSUCCESS; -} - -} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp new file mode 100644 index 0000000000..e53e32c844 --- /dev/null +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +bool +PaymentChannelClaim::checkExtraFeatures(PreflightContext const& ctx) +{ + return !ctx.tx.isFieldPresent(sfCredentialIDs) || ctx.rules.enabled(featureCredentials); +} + +std::uint32_t +PaymentChannelClaim::getFlagsMask(PreflightContext const&) +{ + return tfPaymentChannelClaimMask; +} + +NotTEC +PaymentChannelClaim::preflight(PreflightContext const& ctx) +{ + auto const bal = ctx.tx[~sfBalance]; + if (bal && (!isXRP(*bal) || *bal <= beast::zero)) + return temBAD_AMOUNT; + + auto const amt = ctx.tx[~sfAmount]; + if (amt && (!isXRP(*amt) || *amt <= beast::zero)) + return temBAD_AMOUNT; + + if (bal && amt && *bal > *amt) + return temBAD_AMOUNT; + + { + auto const flags = ctx.tx.getFlags(); + + if (((flags & tfClose) != 0u) && ((flags & tfRenew) != 0u)) + return temMALFORMED; + } + + if (auto const sig = ctx.tx[~sfSignature]) + { + if (!(ctx.tx[~sfPublicKey] && bal)) + return temMALFORMED; + + // Check the signature + // The signature isn't needed if txAccount == src, but if it's + // present, check it + + auto const reqBalance = bal->xrp(); + auto const authAmt = amt ? amt->xrp() : reqBalance; + + if (reqBalance > authAmt) + return temBAD_AMOUNT; + + Keylet const k(ltPAYCHAN, ctx.tx[sfChannel]); + if (!publicKeyType(ctx.tx[sfPublicKey])) + return temMALFORMED; + + PublicKey const pk(ctx.tx[sfPublicKey]); + Serializer msg; + serializePayChanAuthorization(msg, k.key, authAmt); + if (!verify(pk, msg.slice(), *sig)) + return temBAD_SIGNATURE; + } + + if (auto const err = credentials::checkFields(ctx.tx, ctx.j); !isTesSuccess(err)) + return err; + + return tesSUCCESS; +} + +TER +PaymentChannelClaim::preclaim(PreclaimContext const& ctx) +{ + if (!ctx.view.rules().enabled(featureCredentials)) + return Transactor::preclaim(ctx); + + if (auto const err = credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j); + !isTesSuccess(err)) + return err; + + return tesSUCCESS; +} + +TER +PaymentChannelClaim::doApply() +{ + Keylet const k(ltPAYCHAN, ctx_.tx[sfChannel]); + auto const slep = ctx_.view().peek(k); + if (!slep) + return tecNO_TARGET; + + AccountID const src = (*slep)[sfAccount]; + AccountID const dst = (*slep)[sfDestination]; + AccountID const txAccount = ctx_.tx[sfAccount]; + + auto const curExpiration = (*slep)[~sfExpiration]; + { + auto const cancelAfter = (*slep)[~sfCancelAfter]; + auto const closeTime = ctx_.view().header().parentCloseTime.time_since_epoch().count(); + if ((cancelAfter && closeTime >= *cancelAfter) || + (curExpiration && closeTime >= *curExpiration)) + return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.get().getJournal("View")); + } + + if (txAccount != src && txAccount != dst) + return tecNO_PERMISSION; + + if (ctx_.tx[~sfBalance]) + { + auto const chanBalance = slep->getFieldAmount(sfBalance).xrp(); + auto const chanFunds = slep->getFieldAmount(sfAmount).xrp(); + auto const reqBalance = ctx_.tx[sfBalance].xrp(); + + if (txAccount == dst && !ctx_.tx[~sfSignature]) + return temBAD_SIGNATURE; + + if (ctx_.tx[~sfSignature]) + { + PublicKey const pk((*slep)[sfPublicKey]); + if (ctx_.tx[sfPublicKey] != pk) + return temBAD_SIGNER; + } + + if (reqBalance > chanFunds) + return tecUNFUNDED_PAYMENT; + + if (reqBalance <= chanBalance) + { + // nothing requested + return tecUNFUNDED_PAYMENT; + } + + auto const sled = ctx_.view().peek(keylet::account(dst)); + if (!sled) + return tecNO_DST; + + if (auto err = + verifyDepositPreauth(ctx_.tx, ctx_.view(), txAccount, dst, sled, ctx_.journal); + !isTesSuccess(err)) + return err; + + (*slep)[sfBalance] = ctx_.tx[sfBalance]; + XRPAmount const reqDelta = reqBalance - chanBalance; + XRPL_ASSERT( + reqDelta >= beast::zero, "xrpl::PaymentChannelClaim::doApply : minimum balance delta"); + (*sled)[sfBalance] = (*sled)[sfBalance] + reqDelta; + ctx_.view().update(sled); + ctx_.view().update(slep); + } + + if ((ctx_.tx.getFlags() & tfRenew) != 0u) + { + if (src != txAccount) + return tecNO_PERMISSION; + (*slep)[~sfExpiration] = std::nullopt; + ctx_.view().update(slep); + } + + if ((ctx_.tx.getFlags() & tfClose) != 0u) + { + // Channel will close immediately if dry or the receiver closes + if (dst == txAccount || (*slep)[sfBalance] == (*slep)[sfAmount]) + return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.get().getJournal("View")); + + auto const settleExpiration = + ctx_.view().header().parentCloseTime.time_since_epoch().count() + + (*slep)[sfSettleDelay]; + + if (!curExpiration || *curExpiration > settleExpiration) + { + (*slep)[~sfExpiration] = settleExpiration; + ctx_.view().update(slep); + } + } + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp new file mode 100644 index 0000000000..c12169fc0e --- /dev/null +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +/* + PaymentChannel + + Payment channels permit off-ledger checkpoints of XRP payments flowing + in a single direction. A channel sequesters the owner's XRP in its own + ledger entry. The owner can authorize the recipient to claim up to a + given balance by giving the receiver a signed message (off-ledger). The + recipient can use this signed message to claim any unpaid balance while + the channel remains open. The owner can top off the line as needed. If + the channel has not paid out all its funds, the owner must wait out a + delay to close the channel to give the recipient a chance to supply any + claims. The recipient can close the channel at any time. Any transaction + that touches the channel after the expiration time will close the + channel. The total amount paid increases monotonically as newer claims + are issued. When the channel is closed any remaining balance is returned + to the owner. Channels are intended to permit intermittent off-ledger + settlement of ILP trust lines as balances get substantial. For + bidirectional channels, a payment channel can be used in each direction. +*/ + +//------------------------------------------------------------------------------ + +TxConsequences +PaymentChannelCreate::makeTxConsequences(PreflightContext const& ctx) +{ + return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()}; +} + +NotTEC +PaymentChannelCreate::preflight(PreflightContext const& ctx) +{ + if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::zero)) + return temBAD_AMOUNT; + + if (ctx.tx[sfAccount] == ctx.tx[sfDestination]) + return temDST_IS_SRC; + + if (!publicKeyType(ctx.tx[sfPublicKey])) + return temMALFORMED; + + return tesSUCCESS; +} + +TER +PaymentChannelCreate::preclaim(PreclaimContext const& ctx) +{ + auto const account = ctx.tx[sfAccount]; + auto const sle = ctx.view.read(keylet::account(account)); + if (!sle) + return terNO_ACCOUNT; + + // Check reserve and funds availability + { + auto const balance = (*sle)[sfBalance]; + auto const reserve = ctx.view.fees().accountReserve((*sle)[sfOwnerCount] + 1); + + if (balance < reserve) + return tecINSUFFICIENT_RESERVE; + + if (balance < reserve + ctx.tx[sfAmount]) + return tecUNFUNDED; + } + + auto const dst = ctx.tx[sfDestination]; + + { + // Check destination account + auto const sled = ctx.view.read(keylet::account(dst)); + if (!sled) + return tecNO_DST; + + auto const flags = sled->getFlags(); + + // Check if they have disallowed incoming payment channels + if ((flags & lsfDisallowIncomingPayChan) != 0u) + return tecNO_PERMISSION; + + if (((flags & lsfRequireDestTag) != 0u) && !ctx.tx[~sfDestinationTag]) + return tecDST_TAG_NEEDED; + + // Pseudo-accounts cannot receive payment channels, other than native + // to their underlying ledger object - implemented in their respective + // transaction types. Note, this is not amendment-gated because all + // writes to pseudo-account discriminator fields **are** amendment + // gated, hence the behaviour of this check will always match the + // currently active amendments. + if (isPseudoAccount(sled)) + return tecNO_PERMISSION; + } + + return tesSUCCESS; +} + +TER +PaymentChannelCreate::doApply() +{ + auto const account = ctx_.tx[sfAccount]; + auto const sle = ctx_.view().peek(keylet::account(account)); + if (!sle) + return tefINTERNAL; // LCOV_EXCL_LINE + + if (ctx_.view().rules().enabled(fixPayChanCancelAfter)) + { + auto const closeTime = ctx_.view().header().parentCloseTime; + if (ctx_.tx[~sfCancelAfter] && after(closeTime, ctx_.tx[sfCancelAfter])) + return tecEXPIRED; + } + + auto const dst = ctx_.tx[sfDestination]; + + // Create PayChan in ledger. + // + // Note that we we use the value from the sequence or ticket as the + // payChan sequence. For more explanation see comments in SeqProxy.h. + Keylet const payChanKeylet = keylet::payChan(account, dst, ctx_.tx.getSeqValue()); + auto const slep = std::make_shared(payChanKeylet); + + // Funds held in this channel + (*slep)[sfAmount] = ctx_.tx[sfAmount]; + // Amount channel has already paid + (*slep)[sfBalance] = ctx_.tx[sfAmount].zeroed(); + (*slep)[sfAccount] = account; + (*slep)[sfDestination] = dst; + (*slep)[sfSettleDelay] = ctx_.tx[sfSettleDelay]; + (*slep)[sfPublicKey] = ctx_.tx[sfPublicKey]; + (*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter]; + (*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag]; + (*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag]; + if (ctx_.view().rules().enabled(fixIncludeKeyletFields)) + { + (*slep)[sfSequence] = ctx_.tx.getSeqValue(); + } + + ctx_.view().insert(slep); + + // Add PayChan to owner directory + { + auto const page = ctx_.view().dirInsert( + keylet::ownerDir(account), payChanKeylet, describeOwnerDir(account)); + if (!page) + return tecDIR_FULL; // LCOV_EXCL_LINE + (*slep)[sfOwnerNode] = *page; + } + + // Add PayChan to the recipient's owner directory + { + auto const page = + ctx_.view().dirInsert(keylet::ownerDir(dst), payChanKeylet, describeOwnerDir(dst)); + if (!page) + return tecDIR_FULL; // LCOV_EXCL_LINE + (*slep)[sfDestinationNode] = *page; + } + + // Deduct owner's balance, increment owner count + (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; + adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); + ctx_.view().update(sle); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp new file mode 100644 index 0000000000..f8e2399bb1 --- /dev/null +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include + +namespace xrpl { + +TxConsequences +PaymentChannelFund::makeTxConsequences(PreflightContext const& ctx) +{ + return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()}; +} + +NotTEC +PaymentChannelFund::preflight(PreflightContext const& ctx) +{ + if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::zero)) + return temBAD_AMOUNT; + + return tesSUCCESS; +} + +TER +PaymentChannelFund::doApply() +{ + Keylet const k(ltPAYCHAN, ctx_.tx[sfChannel]); + auto const slep = ctx_.view().peek(k); + if (!slep) + return tecNO_ENTRY; + + AccountID const src = (*slep)[sfAccount]; + auto const txAccount = ctx_.tx[sfAccount]; + auto const expiration = (*slep)[~sfExpiration]; + + { + auto const cancelAfter = (*slep)[~sfCancelAfter]; + auto const closeTime = ctx_.view().header().parentCloseTime.time_since_epoch().count(); + if ((cancelAfter && closeTime >= *cancelAfter) || (expiration && closeTime >= *expiration)) + return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.get().getJournal("View")); + } + + if (src != txAccount) + { + // only the owner can add funds or extend + return tecNO_PERMISSION; + } + + if (auto extend = ctx_.tx[~sfExpiration]) + { + auto minExpiration = ctx_.view().header().parentCloseTime.time_since_epoch().count() + + (*slep)[sfSettleDelay]; + if (expiration && *expiration < minExpiration) + minExpiration = *expiration; + + if (*extend < minExpiration) + return temBAD_EXPIRATION; + (*slep)[~sfExpiration] = *extend; + ctx_.view().update(slep); + } + + auto const sle = ctx_.view().peek(keylet::account(txAccount)); + if (!sle) + return tefINTERNAL; // LCOV_EXCL_LINE + + { + // Check reserve and funds availability + auto const balance = (*sle)[sfBalance]; + auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount]); + + if (balance < reserve) + return tecINSUFFICIENT_RESERVE; + + if (balance < reserve + ctx_.tx[sfAmount]) + return tecUNFUNDED; + } + + // do not allow adding funds if dst does not exist + if (AccountID const dst = (*slep)[sfDestination]; !ctx_.view().read(keylet::account(dst))) + { + return tecNO_DST; + } + + (*slep)[sfAmount] = (*slep)[sfAmount] + ctx_.tx[sfAmount]; + ctx_.view().update(slep); + + (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; + ctx_.view().update(sle); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp index 2c7e934fc0..565631b3fd 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp @@ -1,4 +1,5 @@ #include +#include #include #include diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp index c27c884b10..30f24241aa 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp @@ -1,7 +1,9 @@ #include // -#include #include +#include +#include +#include #include #include @@ -98,8 +100,6 @@ PermissionedDomainSet::doApply() Keylet const pdKeylet = keylet::permissionedDomain(account_, ctx_.tx.getFieldU32(sfSequence)); auto slePd = std::make_shared(pdKeylet); - if (!slePd) - return tefINTERNAL; // LCOV_EXCL_LINE slePd->setAccountID(sfOwner, account_); slePd->setFieldU32(sfSequence, ctx_.tx.getFieldU32(sfSequence)); diff --git a/src/libxrpl/tx/transactors/system/Batch.cpp b/src/libxrpl/tx/transactors/system/Batch.cpp index ed3dabec75..cd3ac9a16c 100644 --- a/src/libxrpl/tx/transactors/system/Batch.cpp +++ b/src/libxrpl/tx/transactors/system/Batch.cpp @@ -105,9 +105,13 @@ Batch::calculateBaseFee(ReadView const& view, STTx const& tx) for (STObject const& signer : signers) { if (signer.isFieldPresent(sfTxnSignature)) + { signerCount += 1; + } else if (signer.isFieldPresent(sfSigners)) + { signerCount += signer.getFieldArray(sfSigners).size(); + } } } @@ -119,7 +123,7 @@ Batch::calculateBaseFee(ReadView const& view, STTx const& tx) } // LCOV_EXCL_STOP - XRPAmount signerFees = signerCount * view.fees().base; + XRPAmount const signerFees = signerCount * view.fees().base; // LCOV_EXCL_START if (signerFees > maxAmount - txnFees) @@ -266,7 +270,7 @@ Batch::preflight(PreflightContext const& ctx) return temINVALID_INNER_BATCH; } - if (!(stx.getFlags() & tfInnerBatchTxn)) + if ((stx.getFlags() & tfInnerBatchTxn) == 0u) { JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " << "inner txn must have the tfInnerBatchTxn flag. " @@ -301,7 +305,7 @@ Batch::preflight(PreflightContext const& ctx) auto const innerAccount = stx.getAccountID(sfAccount); if (auto const preflightResult = xrpl::preflight(ctx.registry, ctx.rules, parentBatchId, stx, tapBATCH, ctx.j); - preflightResult.ter != tesSUCCESS) + !isTesSuccess(preflightResult.ter)) { JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " << "inner txn preflight failed: " << transHuman(preflightResult.ter) @@ -331,7 +335,7 @@ Batch::preflight(PreflightContext const& ctx) } // Duplicate sequence and ticket checks - if (flags & (tfAllOrNothing | tfUntilFailure)) + if ((flags & (tfAllOrNothing | tfUntilFailure)) != 0u) { if (auto const seq = stx.getFieldU32(sfSequence); seq != 0) { diff --git a/src/libxrpl/tx/transactors/system/Change.cpp b/src/libxrpl/tx/transactors/system/Change.cpp index d1e2d148d6..a1d9183ca4 100644 --- a/src/libxrpl/tx/transactors/system/Change.cpp +++ b/src/libxrpl/tx/transactors/system/Change.cpp @@ -142,7 +142,7 @@ Change::preCompute() TER Change::applyAmendment() { - uint256 amendment(ctx_.tx.getFieldH256(sfAmendment)); + uint256 const amendment(ctx_.tx.getFieldH256(sfAmendment)); auto const k = keylet::amendments(); @@ -200,7 +200,7 @@ Change::applyAmendment() entry[sfAmendment] = amendment; entry[sfCloseTime] = view().parentCloseTime().time_since_epoch().count(); - if (!ctx_.registry.getAmendmentTable().isSupported(amendment)) + if (!ctx_.registry.get().getAmendmentTable().isSupported(amendment)) { JLOG(j_.warn()) << "Unsupported amendment " << amendment << " received a majority."; } @@ -211,20 +211,24 @@ Change::applyAmendment() amendments.push_back(amendment); amendmentObject->setFieldV256(sfAmendments, amendments); - ctx_.registry.getAmendmentTable().enable(amendment); + ctx_.registry.get().getAmendmentTable().enable(amendment); - if (!ctx_.registry.getAmendmentTable().isSupported(amendment)) + if (!ctx_.registry.get().getAmendmentTable().isSupported(amendment)) { JLOG(j_.error()) << "Unsupported amendment " << amendment << " activated: server blocked."; - ctx_.registry.getOPs().setAmendmentBlocked(); + ctx_.registry.get().getOPs().setAmendmentBlocked(); } } if (newMajorities.empty()) + { amendmentObject->makeFieldAbsent(sfMajorities); + } else + { amendmentObject->setFieldArray(sfMajorities, newMajorities); + } view().update(amendmentObject); @@ -288,7 +292,7 @@ Change::applyUNLModify() return tefFAILURE; } - bool const disabling = ctx_.tx.getFieldU8(sfUNLModifyDisabling); + bool const disabling = ctx_.tx.getFieldU8(sfUNLModifyDisabling) != 0u; auto const seq = ctx_.tx.getFieldU32(sfLedgerSequence); if (seq != view().seq()) { diff --git a/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp b/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp index da04d60142..321e472aa5 100644 --- a/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp +++ b/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp @@ -1,8 +1,8 @@ #include +#include #include #include #include -#include #include namespace xrpl { @@ -35,15 +35,13 @@ LedgerStateFix::calculateBaseFee(ReadView const& view, STTx const& tx) TER LedgerStateFix::preclaim(PreclaimContext const& ctx) { - switch (ctx.tx[sfLedgerFixType]) + if (ctx.tx[sfLedgerFixType] == FixType::nfTokenPageLink) { - case FixType::nfTokenPageLink: { - AccountID const owner{ctx.tx[sfOwner]}; - if (!ctx.view.read(keylet::account(owner))) - return tecOBJECT_NOT_FOUND; + AccountID const owner{ctx.tx[sfOwner]}; + if (!ctx.view.read(keylet::account(owner))) + return tecOBJECT_NOT_FOUND; - return tesSUCCESS; - } + return tesSUCCESS; } // preflight is supposed to verify that only valid FixTypes get to preclaim. @@ -53,13 +51,12 @@ LedgerStateFix::preclaim(PreclaimContext const& ctx) TER LedgerStateFix::doApply() { - switch (ctx_.tx[sfLedgerFixType]) + if (ctx_.tx[sfLedgerFixType] == FixType::nfTokenPageLink) { - case FixType::nfTokenPageLink: - if (!nft::repairNFTokenDirectoryLinks(view(), ctx_.tx[sfOwner])) - return tecFAILED_PROCESSING; + if (!nft::repairNFTokenDirectoryLinks(view(), ctx_.tx[sfOwner])) + return tecFAILED_PROCESSING; - return tesSUCCESS; + return tesSUCCESS; } // preflight is supposed to verify that only valid FixTypes get to doApply. diff --git a/src/libxrpl/tx/transactors/system/CreateTicket.cpp b/src/libxrpl/tx/transactors/system/TicketCreate.cpp similarity index 88% rename from src/libxrpl/tx/transactors/system/CreateTicket.cpp rename to src/libxrpl/tx/transactors/system/TicketCreate.cpp index 7334d88be4..c4e281c357 100644 --- a/src/libxrpl/tx/transactors/system/CreateTicket.cpp +++ b/src/libxrpl/tx/transactors/system/TicketCreate.cpp @@ -1,21 +1,22 @@ -#include #include +#include +#include #include #include #include -#include +#include namespace xrpl { TxConsequences -CreateTicket::makeTxConsequences(PreflightContext const& ctx) +TicketCreate::makeTxConsequences(PreflightContext const& ctx) { // Create TxConsequences identifying the number of sequences consumed. return TxConsequences{ctx.tx, ctx.tx[sfTicketCount]}; } NotTEC -CreateTicket::preflight(PreflightContext const& ctx) +TicketCreate::preflight(PreflightContext const& ctx) { if (std::uint32_t const count = ctx.tx[sfTicketCount]; count < minValidCount || count > maxValidCount) @@ -25,7 +26,7 @@ CreateTicket::preflight(PreflightContext const& ctx) } TER -CreateTicket::preclaim(PreclaimContext const& ctx) +TicketCreate::preclaim(PreclaimContext const& ctx) { auto const id = ctx.tx[sfAccount]; auto const sleAccountRoot = ctx.view.read(keylet::account(id)); @@ -51,7 +52,7 @@ CreateTicket::preclaim(PreclaimContext const& ctx) } TER -CreateTicket::doApply() +TicketCreate::doApply() { SLE::pointer const sleAccountRoot = view().peek(keylet::account(account_)); if (!sleAccountRoot) @@ -65,11 +66,11 @@ CreateTicket::doApply() XRPAmount const reserve = view().fees().accountReserve(sleAccountRoot->getFieldU32(sfOwnerCount) + ticketCount); - if (mPriorBalance < reserve) + if (preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; } - beast::Journal viewJ{ctx_.registry.journal("View")}; + beast::Journal const viewJ{ctx_.registry.get().getJournal("View")}; // The starting ticket sequence is the same as the current account // root sequence. Before we got here to doApply(), the transaction @@ -87,7 +88,7 @@ CreateTicket::doApply() { std::uint32_t const curTicketSeq = firstTicketSeq + i; Keylet const ticketKeylet = keylet::ticket(account_, curTicketSeq); - SLE::pointer sleTicket = std::make_shared(ticketKeylet); + SLE::pointer const sleTicket = std::make_shared(ticketKeylet); sleTicket->setAccountID(sfAccount, account_); sleTicket->setFieldU32(sfTicketSequence, curTicketSeq); diff --git a/src/libxrpl/tx/transactors/token/Clawback.cpp b/src/libxrpl/tx/transactors/token/Clawback.cpp index c64459faa9..d7304e5cf1 100644 --- a/src/libxrpl/tx/transactors/token/Clawback.cpp +++ b/src/libxrpl/tx/transactors/token/Clawback.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include #include @@ -88,7 +90,8 @@ preclaimHelper( // If AllowTrustLineClawback is not set or NoFreeze is set, return no // permission - if (!(issuerFlagsIn & lsfAllowTrustLineClawback) || (issuerFlagsIn & lsfNoFreeze)) + if (((issuerFlagsIn & lsfAllowTrustLineClawback) == 0u) || + ((issuerFlagsIn & lsfNoFreeze) != 0u)) return tecNO_PERMISSION; auto const sleRippleState = @@ -136,7 +139,7 @@ preclaimHelper( if (!sleIssuance) return tecOBJECT_NOT_FOUND; - if (!((*sleIssuance)[sfFlags] & lsfMPTCanClawback)) + if (((*sleIssuance)[sfFlags] & lsfMPTCanClawback) == 0u) return tecNO_PERMISSION; if (sleIssuance->getAccountID(sfIssuer) != issuer) @@ -168,9 +171,13 @@ Clawback::preclaim(PreclaimContext const& ctx) // Note the order of checks - when SAV is active, this check here will make // the one which follows `sleHolder->isFieldPresent(sfAMMID)` redundant. if (ctx.view.rules().enabled(featureSingleAssetVault) && isPseudoAccount(sleHolder)) + { return tecPSEUDO_ACCOUNT; - else if (sleHolder->isFieldPresent(sfAMMID)) + } + if (sleHolder->isFieldPresent(sfAMMID)) + { return tecAMM_ACCOUNT; + } return std::visit( [&](T const&) { @@ -205,7 +212,7 @@ applyHelper(ApplyContext& ctx) fhIGNORE_FREEZE, ctx.journal); - return rippleCredit( + return directSendNoFee( ctx.view(), holder, issuer, std::min(spendableAmount, clawAmount), true, ctx.journal); } @@ -226,7 +233,7 @@ applyHelper(ApplyContext& ctx) ahIGNORE_AUTH, ctx.journal); - return rippleCredit( + return directSendNoFee( ctx.view(), holder, issuer, diff --git a/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp b/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp index 01513e17e0..3519ad0db1 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp @@ -1,4 +1,7 @@ #include +#include +#include +#include #include #include #include @@ -35,7 +38,7 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx) // `holderID` is NOT used if (!holderID) { - std::shared_ptr sleMpt = + std::shared_ptr const sleMpt = ctx.view.read(keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], accountID)); // There is an edge case where all holders have zero balance, issuance @@ -45,7 +48,7 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx) // before fetching the MPTIssuance object. // if holder wants to delete/unauthorize a mpt - if (ctx.tx.getFlags() & tfMPTUnauthorize) + if ((ctx.tx.getFlags() & tfMPTUnauthorize) != 0u) { if (!sleMpt) return tecOBJECT_NOT_FOUND; @@ -113,7 +116,7 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx) // If tx is submitted by issuer, it only applies for MPT with // lsfMPTRequireAuth set - if (!(mptIssuanceFlags & lsfMPTRequireAuth)) + if ((mptIssuanceFlags & lsfMPTRequireAuth) == 0u) return tecNO_AUTH; // The holder must create the MPT before the issuer can authorize it. @@ -129,39 +132,13 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx) return tesSUCCESS; } -TER -MPTokenAuthorize::createMPToken( - ApplyView& view, - MPTID const& mptIssuanceID, - AccountID const& account, - std::uint32_t const flags) -{ - auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); - - auto const ownerNode = - view.dirInsert(keylet::ownerDir(account), mptokenKey, describeOwnerDir(account)); - - if (!ownerNode) - return tecDIR_FULL; // LCOV_EXCL_LINE - - auto mptoken = std::make_shared(mptokenKey); - (*mptoken)[sfAccount] = account; - (*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID; - (*mptoken)[sfFlags] = flags; - (*mptoken)[sfOwnerNode] = *ownerNode; - - view.insert(mptoken); - - return tesSUCCESS; -} - TER MPTokenAuthorize::doApply() { auto const& tx = ctx_.tx; return authorizeMPToken( ctx_.view(), - mPriorBalance, + preFeeBalance_, tx[sfMPTokenIssuanceID], account_, ctx_.journal, diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index cf435a30ca..6bcb1175e8 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include #include @@ -31,8 +33,8 @@ MPTokenIssuanceCreate::preflight(PreflightContext const& ctx) { // If the mutable flags field is included, at least one flag must be // specified. - if (auto const mutableFlags = ctx.tx[~sfMutableFlags]; - mutableFlags && (!*mutableFlags || *mutableFlags & tmfMPTokenIssuanceCreateMutableMask)) + if (auto const mutableFlags = ctx.tx[~sfMutableFlags]; mutableFlags && + ((*mutableFlags == 0u) || ((*mutableFlags & tmfMPTokenIssuanceCreateMutableMask) != 0u))) return temINVALID_FLAG; if (auto const fee = ctx.tx[~sfTransferFee]) @@ -58,7 +60,7 @@ MPTokenIssuanceCreate::preflight(PreflightContext const& ctx) if (auto const metadata = ctx.tx[~sfMPTokenMetadata]) { - if (metadata->length() == 0 || metadata->length() > maxMPTokenMetadataLength) + if (metadata->empty() || metadata->length() > maxMPTokenMetadataLength) return temMALFORMED; } @@ -138,7 +140,7 @@ MPTokenIssuanceCreate::doApply() view(), j_, { - .priorBalance = mPriorBalance, + .priorBalance = preFeeBalance_, .account = account_, .sequence = tx.getSeqValue(), .flags = tx.getFlags(), diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp index 1df4da47ab..8ec1f37886 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp index e6b1ffbbc3..67e0d9077d 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp @@ -1,7 +1,7 @@ +#include #include #include #include -#include #include namespace xrpl { @@ -55,7 +55,7 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx) auto const txFlags = ctx.tx.getFlags(); // fails if both flags are set - if ((txFlags & tfMPTLock) && (txFlags & tfMPTUnlock)) + if (((txFlags & tfMPTLock) != 0u) && ((txFlags & tfMPTUnlock) != 0u)) return temINVALID_FLAG; auto const accountID = ctx.tx[sfAccount]; @@ -77,7 +77,7 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx) return temMALFORMED; // Can not set flags when mutating MPTokenIssuance - if (isMutate && (txFlags & tfUniversalMask)) + if (isMutate && ((txFlags & tfUniversalMask) != 0u)) return temMALFORMED; if (transferFee && *transferFee > maxTransferFee) @@ -88,7 +88,7 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx) if (mutableFlags) { - if (!*mutableFlags || (*mutableFlags & tmfMPTokenIssuanceSetMutableMask)) + if ((*mutableFlags == 0u) || ((*mutableFlags & tmfMPTokenIssuanceSetMutableMask) != 0u)) return temINVALID_FLAG; // Can not set and clear the same flag @@ -102,7 +102,7 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx) // Trying to set a non-zero TransferFee and clear MPTCanTransfer // in the same transaction is not allowed. - if (transferFee.value_or(0) && (*mutableFlags & tmfMPTClearCanTransfer)) + if ((transferFee.value_or(0) != 0u) && ((*mutableFlags & tmfMPTClearCanTransfer) != 0u)) return temMALFORMED; } } @@ -123,23 +123,23 @@ MPTokenIssuanceSet::checkPermission(ReadView const& view, STTx const& tx) if (!sle) return terNO_DELEGATE_PERMISSION; - if (checkTxPermission(sle, tx) == tesSUCCESS) + if (isTesSuccess(checkTxPermission(sle, tx))) return tesSUCCESS; auto const txFlags = tx.getFlags(); // this is added in case more flags will be added for MPTokenIssuanceSet // in the future. Currently unreachable. - if (txFlags & tfMPTokenIssuanceSetMask) + if ((txFlags & tfMPTokenIssuanceSetMask) != 0u) return terNO_DELEGATE_PERMISSION; // LCOV_EXCL_LINE std::unordered_set granularPermissions; loadGranularPermission(sle, ttMPTOKEN_ISSUANCE_SET, granularPermissions); - if (txFlags & tfMPTLock && !granularPermissions.contains(MPTokenIssuanceLock)) + if (((txFlags & tfMPTLock) != 0u) && !granularPermissions.contains(MPTokenIssuanceLock)) return terNO_DELEGATE_PERMISSION; - if (txFlags & tfMPTUnlock && !granularPermissions.contains(MPTokenIssuanceUnlock)) + if (((txFlags & tfMPTUnlock) != 0u) && !granularPermissions.contains(MPTokenIssuanceUnlock)) return terNO_DELEGATE_PERMISSION; return tesSUCCESS; @@ -158,9 +158,13 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx) // For readability two separate `if` rather than `||` of two conditions if (!ctx.view.rules().enabled(featureSingleAssetVault) && !ctx.view.rules().enabled(featureDynamicMPT)) + { return tecNO_PERMISSION; - else if (ctx.tx.isFlag(tfMPTLock) || ctx.tx.isFlag(tfMPTUnlock)) + } + if (ctx.tx.isFlag(tfMPTLock) || ctx.tx.isFlag(tfMPTUnlock)) + { return tecNO_PERMISSION; + } } // ensure it is issued by the tx submitter @@ -240,9 +244,13 @@ MPTokenIssuanceSet::doApply() std::shared_ptr sle; if (holderID) + { sle = view().peek(keylet::mptoken(mptIssuanceID, *holderID)); + } else + { sle = view().peek(keylet::mptIssuance(mptIssuanceID)); + } if (!sle) return tecINTERNAL; // LCOV_EXCL_LINE @@ -250,22 +258,30 @@ MPTokenIssuanceSet::doApply() std::uint32_t const flagsIn = sle->getFieldU32(sfFlags); std::uint32_t flagsOut = flagsIn; - if (txFlags & tfMPTLock) + if ((txFlags & tfMPTLock) != 0u) + { flagsOut |= lsfMPTLocked; - else if (txFlags & tfMPTUnlock) + } + else if ((txFlags & tfMPTUnlock) != 0u) + { flagsOut &= ~lsfMPTLocked; + } if (auto const mutableFlags = ctx_.tx[~sfMutableFlags].value_or(0)) { for (auto const& f : mptMutabilityFlags) { - if (mutableFlags & f.setFlag) + if ((mutableFlags & f.setFlag) != 0u) + { flagsOut |= f.canMutateFlag; - else if (mutableFlags & f.clearFlag) + } + else if ((mutableFlags & f.clearFlag) != 0u) + { flagsOut &= ~f.canMutateFlag; + } } - if (mutableFlags & tmfMPTClearCanTransfer) + if ((mutableFlags & tmfMPTClearCanTransfer) != 0u) { // If the lsfMPTCanTransfer flag is being cleared, then also clear // the TransferFee field. @@ -283,17 +299,25 @@ MPTokenIssuanceSet::doApply() // - If the field is present, it must be non-zero. // Therefore, when TransferFee is 0, the field should be removed. if (transferFee == 0) + { sle->makeFieldAbsent(sfTransferFee); + } else + { sle->setFieldU16(sfTransferFee, *transferFee); + } } if (auto const metadata = ctx_.tx[~sfMPTokenMetadata]) { if (metadata->empty()) + { sle->makeFieldAbsent(sfMPTokenMetadata); + } else + { sle->setFieldVL(sfMPTokenMetadata, *metadata); + } } if (domainID) diff --git a/src/libxrpl/tx/transactors/token/SetTrust.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp similarity index 81% rename from src/libxrpl/tx/transactors/token/SetTrust.cpp rename to src/libxrpl/tx/transactors/token/TrustSet.cpp index b6ae288a8c..801645d1c0 100644 --- a/src/libxrpl/tx/transactors/token/SetTrust.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -1,13 +1,14 @@ -#include #include +#include +#include +#include #include #include #include #include #include #include -#include -#include +#include namespace { @@ -46,13 +47,13 @@ computeFreezeFlags( namespace xrpl { std::uint32_t -SetTrust::getFlagsMask(PreflightContext const& ctx) +TrustSet::getFlagsMask(PreflightContext const& ctx) { return tfTrustSetMask; } NotTEC -SetTrust::preflight(PreflightContext const& ctx) +TrustSet::preflight(PreflightContext const& ctx) { auto& tx = ctx.tx; auto& j = ctx.j; @@ -63,7 +64,7 @@ SetTrust::preflight(PreflightContext const& ctx) { // Even though the deep freeze flags are included in the // `tfTrustSetMask`, they are not valid if the amendment is not enabled. - if (uTxFlags & (tfSetDeepFreeze | tfClearDeepFreeze)) + if ((uTxFlags & (tfSetDeepFreeze | tfClearDeepFreeze)) != 0u) { return temINVALID_FLAG; } @@ -106,7 +107,7 @@ SetTrust::preflight(PreflightContext const& ctx) } NotTEC -SetTrust::checkPermission(ReadView const& view, STTx const& tx) +TrustSet::checkPermission(ReadView const& view, STTx const& tx) { auto const delegate = tx[~sfDelegate]; if (!delegate) @@ -118,7 +119,7 @@ SetTrust::checkPermission(ReadView const& view, STTx const& tx) if (!sle) return terNO_DELEGATE_PERMISSION; - if (checkTxPermission(sle, tx) == tesSUCCESS) + if (isTesSuccess(checkTxPermission(sle, tx))) return tesSUCCESS; std::uint32_t const txFlags = tx.getFlags(); @@ -126,7 +127,7 @@ SetTrust::checkPermission(ReadView const& view, STTx const& tx) // Currently we only support TrustlineAuthorize, TrustlineFreeze and // TrustlineUnfreeze granular permission. Setting other flags returns // error. - if (txFlags & tfTrustSetPermissionMask) + if ((txFlags & tfTrustSetPermissionMask) != 0u) return terNO_DELEGATE_PERMISSION; if (tx.isFieldPresent(sfQualityIn) || tx.isFieldPresent(sfQualityOut)) @@ -144,11 +145,11 @@ SetTrust::checkPermission(ReadView const& view, STTx const& tx) std::unordered_set granularPermissions; loadGranularPermission(sle, ttTRUST_SET, granularPermissions); - if (txFlags & tfSetfAuth && !granularPermissions.contains(TrustlineAuthorize)) + if (((txFlags & tfSetfAuth) != 0u) && !granularPermissions.contains(TrustlineAuthorize)) return terNO_DELEGATE_PERMISSION; - if (txFlags & tfSetFreeze && !granularPermissions.contains(TrustlineFreeze)) + if (((txFlags & tfSetFreeze) != 0u) && !granularPermissions.contains(TrustlineFreeze)) return terNO_DELEGATE_PERMISSION; - if (txFlags & tfClearFreeze && !granularPermissions.contains(TrustlineUnfreeze)) + if (((txFlags & tfClearFreeze) != 0u) && !granularPermissions.contains(TrustlineUnfreeze)) return terNO_DELEGATE_PERMISSION; // updating LimitAmount is not allowed only with granular permissions, @@ -167,7 +168,7 @@ SetTrust::checkPermission(ReadView const& view, STTx const& tx) } TER -SetTrust::preclaim(PreclaimContext const& ctx) +TrustSet::preclaim(PreclaimContext const& ctx) { auto const id = ctx.tx[sfAccount]; @@ -177,9 +178,9 @@ SetTrust::preclaim(PreclaimContext const& ctx) std::uint32_t const uTxFlags = ctx.tx.getFlags(); - bool const bSetAuth = (uTxFlags & tfSetfAuth); + bool const bSetAuth = (uTxFlags & tfSetfAuth) != 0u; - if (bSetAuth && !(sle->getFieldU32(sfFlags) & lsfRequireAuth)) + if (bSetAuth && ((sle->getFieldU32(sfFlags) & lsfRequireAuth) == 0u)) { JLOG(ctx.j.trace()) << "Retry: Auth not required."; return tefNO_AUTH_REQUIRED; @@ -201,7 +202,7 @@ SetTrust::preclaim(PreclaimContext const& ctx) // If the destination has opted to disallow incoming trustlines // then honour that flag - if (sleDst->getFlags() & lsfDisallowIncomingTrustline) + if ((sleDst->getFlags() & lsfDisallowIncomingTrustline) != 0u) { // The original implementation of featureDisallowIncoming was // too restrictive. If @@ -214,7 +215,9 @@ SetTrust::preclaim(PreclaimContext const& ctx) // pass } else + { return tecNO_PERMISSION; + } } // In general, trust lines to pseudo accounts are not permitted, unless @@ -224,7 +227,7 @@ SetTrust::preclaim(PreclaimContext const& ctx) if (sleDst && isPseudoAccount(sleDst)) { // If destination is AMM and the trustline doesn't exist then only allow - // SetTrust if the asset is AMM LP token and AMM is not in empty state. + // TrustSet if the asset is AMM LP token and AMM is not in empty state. if (sleDst->isFieldPresent(sfAMMID)) { if (ctx.view.exists(keylet::line(id, uDstAccountID, currency))) @@ -233,14 +236,20 @@ SetTrust::preclaim(PreclaimContext const& ctx) } else if (auto const ammSle = ctx.view.read({ltAMM, sleDst->getFieldH256(sfAMMID)})) { - if (auto const lpTokens = ammSle->getFieldAmount(sfLPTokenBalance); - lpTokens == beast::zero) + auto const lpTokens = ammSle->getFieldAmount(sfLPTokenBalance); + if (lpTokens == beast::zero) + { return tecAMM_EMPTY; - else if (lpTokens.getCurrency() != saLimitAmount.getCurrency()) + } + if (lpTokens.getCurrency() != saLimitAmount.getCurrency()) + { return tecNO_PERMISSION; + } } else + { return tecINTERNAL; // LCOV_EXCL_LINE + } } else if (sleDst->isFieldPresent(sfVaultID) || sleDst->isFieldPresent(sfLoanBrokerID)) { @@ -249,15 +258,17 @@ SetTrust::preclaim(PreclaimContext const& ctx) // else pass } else + { return tecPSEUDO_ACCOUNT; + } } // Checking all freeze/deep freeze flag invariants. if (ctx.view.rules().enabled(featureDeepFreeze)) { bool const bNoFreeze = sle->isFlag(lsfNoFreeze); - bool const bSetFreeze = (uTxFlags & tfSetFreeze); - bool const bSetDeepFreeze = (uTxFlags & tfSetDeepFreeze); + bool const bSetFreeze = (uTxFlags & tfSetFreeze) != 0u; + bool const bSetDeepFreeze = (uTxFlags & tfSetDeepFreeze) != 0u; if (bNoFreeze && (bSetFreeze || bSetDeepFreeze)) { @@ -265,8 +276,8 @@ SetTrust::preclaim(PreclaimContext const& ctx) return tecNO_PERMISSION; } - bool const bClearFreeze = (uTxFlags & tfClearFreeze); - bool const bClearDeepFreeze = (uTxFlags & tfClearDeepFreeze); + bool const bClearFreeze = (uTxFlags & tfClearFreeze) != 0u; + bool const bClearDeepFreeze = (uTxFlags & tfClearDeepFreeze) != 0u; if ((bSetFreeze || bSetDeepFreeze) && (bClearFreeze || bClearDeepFreeze)) { // Freezing and unfreezing in the same transaction should be @@ -288,7 +299,7 @@ SetTrust::preclaim(PreclaimContext const& ctx) // Trying to set deep freeze on not already frozen trust line must // fail. This also checks that clearing normal freeze while deep // frozen must not work - if (deepFrozen && !frozen) + if ((deepFrozen != 0u) && (frozen == 0u)) { return tecNO_PERMISSION; } @@ -298,7 +309,7 @@ SetTrust::preclaim(PreclaimContext const& ctx) } TER -SetTrust::doApply() +TrustSet::doApply() { TER terResult = tesSUCCESS; @@ -307,7 +318,7 @@ SetTrust::doApply() bool const bQualityOut(ctx_.tx.isFieldPresent(sfQualityOut)); Currency const currency(saLimitAmount.getCurrency()); - AccountID uDstAccountID(saLimitAmount.getIssuer()); + AccountID const uDstAccountID(saLimitAmount.getIssuer()); // true, if current is high account. bool const bHigh = account_ > uDstAccountID; @@ -325,7 +336,7 @@ SetTrust::doApply() // items. // // We do this because being able to exchange currencies, - // which needs trust lines, is a powerful Ripple feature. + // which needs trust lines, is a powerful XRPL feature. // So we want to make it easy for a gateway to fund the // accounts of its users without fear of being tricked. // @@ -339,7 +350,7 @@ SetTrust::doApply() XRPAmount const reserveCreate( (uOwnerCount < 2) ? XRPAmount(beast::zero) : view().fees().accountReserve(uOwnerCount + 1)); - std::uint32_t uQualityIn(bQualityIn ? ctx_.tx.getFieldU32(sfQualityIn) : 0); + std::uint32_t const uQualityIn(bQualityIn ? ctx_.tx.getFieldU32(sfQualityIn) : 0); std::uint32_t uQualityOut(bQualityOut ? ctx_.tx.getFieldU32(sfQualityOut) : 0); if (bQualityOut && QUALITY_ONE == uQualityOut) @@ -347,17 +358,17 @@ SetTrust::doApply() std::uint32_t const uTxFlags = ctx_.tx.getFlags(); - bool const bSetAuth = (uTxFlags & tfSetfAuth); - bool const bSetNoRipple = (uTxFlags & tfSetNoRipple); - bool const bClearNoRipple = (uTxFlags & tfClearNoRipple); - bool const bSetFreeze = (uTxFlags & tfSetFreeze); - bool const bClearFreeze = (uTxFlags & tfClearFreeze); - bool const bSetDeepFreeze = (uTxFlags & tfSetDeepFreeze); - bool const bClearDeepFreeze = (uTxFlags & tfClearDeepFreeze); + bool const bSetAuth = (uTxFlags & tfSetfAuth) != 0u; + bool const bSetNoRipple = (uTxFlags & tfSetNoRipple) != 0u; + bool const bClearNoRipple = (uTxFlags & tfClearNoRipple) != 0u; + bool const bSetFreeze = (uTxFlags & tfSetFreeze) != 0u; + bool const bClearFreeze = (uTxFlags & tfClearFreeze) != 0u; + bool const bSetDeepFreeze = (uTxFlags & tfSetDeepFreeze) != 0u; + bool const bClearDeepFreeze = (uTxFlags & tfClearDeepFreeze) != 0u; - auto viewJ = ctx_.registry.journal("View"); + auto viewJ = ctx_.registry.get().getJournal("View"); - SLE::pointer sleDst = view().peek(keylet::account(uDstAccountID)); + SLE::pointer const sleDst = view().peek(keylet::account(uDstAccountID)); if (!sleDst) { @@ -368,7 +379,8 @@ SetTrust::doApply() STAmount saLimitAllow = saLimitAmount; saLimitAllow.setIssuer(account_); - SLE::pointer sleRippleState = view().peek(keylet::line(account_, uDstAccountID, currency)); + SLE::pointer const sleRippleState = + view().peek(keylet::line(account_, uDstAccountID, currency)); if (sleRippleState) { @@ -376,10 +388,10 @@ SetTrust::doApply() STAmount saLowLimit; STAmount saHighBalance; STAmount saHighLimit; - std::uint32_t uLowQualityIn; - std::uint32_t uLowQualityOut; - std::uint32_t uHighQualityIn; - std::uint32_t uHighQualityOut; + std::uint32_t uLowQualityIn = 0; + std::uint32_t uLowQualityOut = 0; + std::uint32_t uHighQualityIn = 0; + std::uint32_t uHighQualityOut = 0; auto const& uLowAccountID = !bHigh ? account_ : uDstAccountID; auto const& uHighAccountID = bHigh ? account_ : uDstAccountID; SLE::ref sleLowAccount = !bHigh ? sle : sleDst; @@ -412,7 +424,7 @@ SetTrust::doApply() uLowQualityIn = sleRippleState->getFieldU32(sfLowQualityIn); uHighQualityIn = sleRippleState->getFieldU32(sfHighQualityIn); } - else if (uQualityIn) + else if (uQualityIn != 0u) { // Setting. @@ -448,7 +460,7 @@ SetTrust::doApply() uLowQualityOut = sleRippleState->getFieldU32(sfLowQualityOut); uHighQualityOut = sleRippleState->getFieldU32(sfHighQualityOut); } - else if (uQualityOut) + else if (uQualityOut != 0u) { // Setting. @@ -473,11 +485,14 @@ SetTrust::doApply() if (bSetNoRipple && !bClearNoRipple) { if ((bHigh ? saHighBalance : saLowBalance) >= beast::zero) + { uFlagsOut |= (bHigh ? lsfHighNoRipple : lsfLowNoRipple); - + } else + { // Cannot set noRipple on a negative balance. return tecNO_PERMISSION; + } } else if (bClearNoRipple && !bSetNoRipple) { @@ -501,23 +516,23 @@ SetTrust::doApply() if (QUALITY_ONE == uHighQualityOut) uHighQualityOut = 0; - bool const bLowDefRipple = sleLowAccount->getFlags() & lsfDefaultRipple; - bool const bHighDefRipple = sleHighAccount->getFlags() & lsfDefaultRipple; + bool const bLowDefRipple = (sleLowAccount->getFlags() & lsfDefaultRipple) != 0u; + bool const bHighDefRipple = (sleHighAccount->getFlags() & lsfDefaultRipple) != 0u; - bool const bLowReserveSet = uLowQualityIn || uLowQualityOut || - ((uFlagsOut & lsfLowNoRipple) == 0) != bLowDefRipple || (uFlagsOut & lsfLowFreeze) || - saLowLimit || saLowBalance > beast::zero; + bool const bLowReserveSet = (uLowQualityIn != 0u) || (uLowQualityOut != 0u) || + ((uFlagsOut & lsfLowNoRipple) == 0) != bLowDefRipple || + ((uFlagsOut & lsfLowFreeze) != 0u) || saLowLimit || saLowBalance > beast::zero; bool const bLowReserveClear = !bLowReserveSet; - bool const bHighReserveSet = uHighQualityIn || uHighQualityOut || - ((uFlagsOut & lsfHighNoRipple) == 0) != bHighDefRipple || (uFlagsOut & lsfHighFreeze) || - saHighLimit || saHighBalance > beast::zero; + bool const bHighReserveSet = (uHighQualityIn != 0u) || (uHighQualityOut != 0u) || + ((uFlagsOut & lsfHighNoRipple) == 0) != bHighDefRipple || + ((uFlagsOut & lsfHighFreeze) != 0u) || saHighLimit || saHighBalance > beast::zero; bool const bHighReserveClear = !bHighReserveSet; bool const bDefault = bLowReserveClear && bHighReserveClear; - bool const bLowReserved = (uFlagsIn & lsfLowReserve); - bool const bHighReserved = (uFlagsIn & lsfHighReserve); + bool const bLowReserved = (uFlagsIn & lsfLowReserve) != 0u; + bool const bHighReserved = (uFlagsIn & lsfHighReserve) != 0u; bool bReserveIncrease = false; @@ -570,7 +585,7 @@ SetTrust::doApply() terResult = trustDelete(view(), sleRippleState, uLowAccountID, uHighAccountID, viewJ); } // Reserve is not scaled by load. - else if (bReserveIncrease && mPriorBalance < reserveCreate) + else if (bReserveIncrease && preFeeBalance_ < reserveCreate) { JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " "add trust line."; @@ -588,18 +603,18 @@ SetTrust::doApply() } // Line does not exist. else if ( - !saLimitAmount && // Setting default limit. - (!bQualityIn || !uQualityIn) && // Not setting quality in or - // setting default quality in. - (!bQualityOut || !uQualityOut) && // Not setting quality out or - // setting default quality out. + !saLimitAmount && // Setting default limit. + (!bQualityIn || (uQualityIn == 0u)) && // Not setting quality in or + // setting default quality in. + (!bQualityOut || (uQualityOut == 0u)) && // Not setting quality out or + // setting default quality out. (!bSetAuth)) { JLOG(j_.trace()) << "Redundant: Setting non-existent ripple line to defaults."; return tecNO_LINE_REDUNDANT; } - else if (mPriorBalance < reserveCreate) // Reserve is not scaled by - // load. + else if (preFeeBalance_ < reserveCreate) // Reserve is not scaled by + // load. { JLOG(j_.trace()) << "Delay transaction: Line does not exist. " "Insufficent reserve to create line."; @@ -611,7 +626,7 @@ SetTrust::doApply() else { // Zero balance in currency. - STAmount saBalance(Issue{currency, noAccount()}); + STAmount const saBalance(Issue{currency, noAccount()}); auto const k = keylet::line(account_, uDstAccountID, currency); diff --git a/src/libxrpl/tx/transactors/vault/VaultClawback.cpp b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp index bfce93b1dd..9904c4183d 100644 --- a/src/libxrpl/tx/transactors/vault/VaultClawback.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include #include @@ -11,6 +13,7 @@ #include #include +#include namespace xrpl { NotTEC @@ -27,8 +30,10 @@ VaultClawback::preflight(PreflightContext const& ctx) { // Note, zero amount is valid, it means "all". It is also the default. if (*amount < beast::zero) + { return temBAD_AMOUNT; - else if (isXRP(amount->asset())) + } + if (isXRP(amount->asset())) { JLOG(ctx.j.debug()) << "VaultClawback: cannot clawback XRP."; return temMALFORMED; @@ -222,7 +227,11 @@ VaultClawback::assetsToClawback( auto const mptIssuanceID = *vault->at(sfShareMPTID); MPTIssue const share{mptIssuanceID}; - if (clawbackAmount == beast::zero) + // Pre-fixSecurity3_1_3: zero-amount clawback returned early without + // clamping to assetsAvailable, allowing more assets to be recovered + // than available when there was an outstanding loan. Retained for + // ledger replay compatibility. + if (!ctx_.view().rules().enabled(fixSecurity3_1_3) && clawbackAmount == beast::zero) { auto const sharesDestroyed = accountHolds( view(), @@ -239,22 +248,40 @@ VaultClawback::assetsToClawback( } STAmount sharesDestroyed; - STAmount assetsRecovered = clawbackAmount; + STAmount assetsRecovered; + try { + if (clawbackAmount == beast::zero) + { + sharesDestroyed = accountHolds( + view(), + holder, + share, + FreezeHandling::fhIGNORE_FREEZE, + AuthHandling::ahIGNORE_AUTH, + j_); + auto const maybeAssets = + sharesToAssetsWithdraw(vault, sleShareIssuance, sharesDestroyed); + if (!maybeAssets) + return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + + assetsRecovered = *maybeAssets; + } + else { auto const maybeShares = - assetsToSharesWithdraw(vault, sleShareIssuance, assetsRecovered); + assetsToSharesWithdraw(vault, sleShareIssuance, clawbackAmount); if (!maybeShares) return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE sharesDestroyed = *maybeShares; + + auto const maybeAssets = + sharesToAssetsWithdraw(vault, sleShareIssuance, sharesDestroyed); + if (!maybeAssets) + return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + assetsRecovered = *maybeAssets; } - - auto const maybeAssets = sharesToAssetsWithdraw(vault, sleShareIssuance, sharesDestroyed); - if (!maybeAssets) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE - assetsRecovered = *maybeAssets; - // Clamp to maximum. if (assetsRecovered > *assetsAvailable) { @@ -330,7 +357,7 @@ VaultClawback::doApply() lossUnrealized <= (assetsTotal - assetsAvailable), "xrpl::VaultClawback::doApply : loss and assets do balance"); - AccountID holder = tx[sfHolder]; + AccountID const holder = tx[sfHolder]; STAmount sharesDestroyed = {share}; STAmount assetsRecovered = {vault->at(sfAsset)}; diff --git a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp index b1f227ddf3..02f8ecb57b 100644 --- a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp @@ -1,4 +1,7 @@ #include +#include +#include +#include #include #include #include @@ -50,9 +53,13 @@ VaultCreate::preflight(PreflightContext const& ctx) if (auto const domain = ctx.tx[~sfDomainID]) { if (*domain == beast::zero) + { return temMALFORMED; - else if ((ctx.tx.getFlags() & tfVaultPrivate) == 0) + } + if ((ctx.tx.getFlags() & tfVaultPrivate) == 0) + { return temMALFORMED; // DomainID only allowed on private vaults + } } if (auto const assetMax = ctx.tx[~sfAssetsMaximum]) @@ -63,7 +70,7 @@ VaultCreate::preflight(PreflightContext const& ctx) if (auto const metadata = ctx.tx[~sfMPTokenMetadata]) { - if (metadata->length() == 0 || metadata->length() > maxMPTokenMetadataLength) + if (metadata->empty() || metadata->length() > maxMPTokenMetadataLength) return temMALFORMED; } @@ -137,7 +144,7 @@ VaultCreate::doApply() // We will create Vault and PseudoAccount, hence increase OwnerCount by 2 adjustOwnerCount(view(), owner, 2, j_); auto const ownerCount = owner->at(sfOwnerCount); - if (mPriorBalance < view().fees().accountReserve(ownerCount)) + if (preFeeBalance_ < view().fees().accountReserve(ownerCount)) return tecINSUFFICIENT_RESERVE; auto maybePseudo = createPseudoAccount(view(), vault->key(), sfVaultID); @@ -147,7 +154,7 @@ VaultCreate::doApply() auto pseudoId = pseudo->at(sfAccount); auto asset = tx[sfAsset]; - if (auto ter = addEmptyHolding(view(), pseudoId, mPriorBalance, asset, j_); !isTesSuccess(ter)) + if (auto ter = addEmptyHolding(view(), pseudoId, preFeeBalance_, asset, j_); !isTesSuccess(ter)) return ter; std::uint8_t const scale = (asset.holds() || asset.native()) @@ -158,7 +165,7 @@ VaultCreate::doApply() std::uint32_t mptFlags = 0; if ((txFlags & tfVaultShareNonTransferable) == 0) mptFlags |= (lsfMPTCanEscrow | lsfMPTCanTrade | lsfMPTCanTransfer); - if (txFlags & tfVaultPrivate) + if ((txFlags & tfVaultPrivate) != 0u) mptFlags |= lsfMPTRequireAuth; // Note, here we are **not** creating an MPToken for the assets held in @@ -197,24 +204,28 @@ VaultCreate::doApply() vault->at(sfData) = *value; // Required field, default to vaultStrategyFirstComeFirstServe if (auto value = tx[~sfWithdrawalPolicy]) + { vault->at(sfWithdrawalPolicy) = *value; + } else + { vault->at(sfWithdrawalPolicy) = vaultStrategyFirstComeFirstServe; - if (scale) + } + if (scale != 0u) vault->at(sfScale) = scale; view().insert(vault); // Explicitly create MPToken for the vault owner if (auto const err = - authorizeMPToken(view(), mPriorBalance, mptIssuanceID, account_, ctx_.journal); + authorizeMPToken(view(), preFeeBalance_, mptIssuanceID, account_, ctx_.journal); !isTesSuccess(err)) return err; // If the vault is private, set the authorized flag for the vault owner - if (txFlags & tfVaultPrivate) + if ((txFlags & tfVaultPrivate) != 0u) { if (auto const err = authorizeMPToken( - view(), mPriorBalance, mptIssuanceID, pseudoId, ctx_.journal, {}, account_); + view(), preFeeBalance_, mptIssuanceID, pseudoId, ctx_.journal, {}, account_); !isTesSuccess(err)) return err; } diff --git a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp index ad39ed9cf2..5a60c0032c 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp @@ -1,4 +1,7 @@ #include +#include +#include +#include #include #include #include @@ -52,7 +55,7 @@ VaultDelete::preclaim(PreclaimContext const& ctx) if (!sleMPT) { // LCOV_EXCL_START - JLOG(ctx.j.error()) << "VaultDeposit: missing issuance of vault shares."; + JLOG(ctx.j.error()) << "VaultDelete: missing issuance of vault shares."; return tecOBJECT_NOT_FOUND; // LCOV_EXCL_STOP } @@ -60,7 +63,7 @@ VaultDelete::preclaim(PreclaimContext const& ctx) if (sleMPT->at(sfIssuer) != vault->getAccountID(sfAccount)) { // LCOV_EXCL_START - JLOG(ctx.j.error()) << "VaultDeposit: invalid owner of vault shares."; + JLOG(ctx.j.error()) << "VaultDelete: invalid owner of vault shares."; return tecNO_PERMISSION; // LCOV_EXCL_STOP } @@ -197,8 +200,6 @@ VaultDelete::doApply() // Destroy the vault. view().erase(vault); - associateAsset(*vault, asset); - return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp index 58912f5010..04b249d211 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp @@ -1,5 +1,8 @@ -#include #include +#include +#include +#include +#include #include #include #include @@ -101,7 +104,9 @@ VaultDeposit::preclaim(PreclaimContext const& ctx) return err; } else + { return tecNO_AUTH; + } } // Source MPToken must exist (if asset is an MPT) @@ -146,7 +151,7 @@ VaultDeposit::doApply() if (vault->isFlag(lsfVaultPrivate) && account_ != vault->at(sfOwner)) { if (auto const err = enforceMPTokenAuthorization( - ctx_.view(), mptIssuanceID, account_, mPriorBalance, j_); + ctx_.view(), mptIssuanceID, account_, preFeeBalance_, j_); !isTesSuccess(err)) return err; } @@ -156,7 +161,7 @@ VaultDeposit::doApply() if (!view().exists(keylet::mptoken(mptIssuanceID, account_))) { if (auto const err = authorizeMPToken( - view(), mPriorBalance, mptIssuanceID->value(), account_, ctx_.journal); + view(), preFeeBalance_, mptIssuanceID->value(), account_, ctx_.journal); !isTesSuccess(err)) return err; } @@ -169,7 +174,7 @@ VaultDeposit::doApply() account_ == vault->at(sfOwner), "xrpl::VaultDeposit::doApply : account is owner"); if (auto const err = authorizeMPToken( view(), - mPriorBalance, // priorBalance + preFeeBalance_, // priorBalance mptIssuanceID->value(), // mptIssuanceID sleIssuance->at(sfIssuer), // account ctx_.journal, @@ -196,8 +201,10 @@ VaultDeposit::doApply() auto const maybeAssets = sharesToAssetsDeposit(vault, sleIssuance, sharesCreated); if (!maybeAssets) + { return tecINTERNAL; // LCOV_EXCL_LINE - else if (*maybeAssets > amount) + } + if (*maybeAssets > amount) { // LCOV_EXCL_START JLOG(j_.error()) << "VaultDeposit: would take more than offered."; diff --git a/src/libxrpl/tx/transactors/vault/VaultSet.cpp b/src/libxrpl/tx/transactors/vault/VaultSet.cpp index 532edcdcef..b54389bcde 100644 --- a/src/libxrpl/tx/transactors/vault/VaultSet.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultSet.cpp @@ -14,10 +14,7 @@ namespace xrpl { bool VaultSet::checkExtraFeatures(PreflightContext const& ctx) { - if (ctx.tx.isFieldPresent(sfDomainID) && !ctx.rules.enabled(featurePermissionedDomains)) - return false; - - return true; + return !ctx.tx.isFieldPresent(sfDomainID) || ctx.rules.enabled(featurePermissionedDomains); } NotTEC diff --git a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp index 9f808c2dfc..f6b8c98788 100644 --- a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp @@ -1,5 +1,7 @@ -#include #include +#include +#include +#include #include #include #include @@ -41,10 +43,10 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx) if (!vault) return tecNO_ENTRY; - auto const assets = ctx.tx[sfAmount]; + auto const amount = ctx.tx[sfAmount]; auto const vaultAsset = vault->at(sfAsset); auto const vaultShare = vault->at(sfShareMPTID); - if (assets.asset() != vaultAsset && assets.asset() != vaultShare) + if (amount.asset() != vaultAsset && amount.asset() != vaultShare) return tecWRONG_ASSET; auto const& vaultAccount = vault->at(sfAccount); @@ -65,8 +67,53 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx) // LCOV_EXCL_STOP } - if (auto const ret = canWithdraw(ctx.view, ctx.tx)) - return ret; + if (ctx.view.rules().enabled(fixSecurity3_1_3) && amount.asset() == vaultShare) + { + // Post-fixSecurity3_1_3: if the user specified shares, convert + // to the equivalent asset amount before checking withdrawal + // limits. Pre-amendment the limit check was skipped for + // share-denominated withdrawals. + auto const sleIssuance = ctx.view.read(keylet::mptIssuance(vaultShare)); + if (!sleIssuance) + { + // LCOV_EXCL_START + JLOG(ctx.j.error()) << "VaultWithdraw: missing issuance of vault shares."; + return tefINTERNAL; + // LCOV_EXCL_STOP + } + + try + { + auto const maybeAssets = sharesToAssetsWithdraw(vault, sleIssuance, amount); + if (!maybeAssets) + return tefINTERNAL; // LCOV_EXCL_LINE + + if (auto const ret = canWithdraw( + ctx.view, + account, + dstAcct, + *maybeAssets, + ctx.tx.isFieldPresent(sfDestinationTag))) + return ret; + } + catch (std::overflow_error const&) + { + // It's easy to hit this exception from Number with large enough Scale + // so we avoid spamming the log and only use debug here. + JLOG(ctx.j.debug()) // + << "VaultWithdraw: overflow error with" + << " scale=" << (int)vault->at(sfScale) // + << ", assetsTotal=" << vault->at(sfAssetsTotal) + << ", sharesTotal=" << sleIssuance->at(sfOutstandingAmount) + << ", amount=" << amount.value(); + return tecPATH_DRY; + } + } + else + { + if (auto const ret = canWithdraw(ctx.view, ctx.tx)) + return ret; + } // If sending to Account (i.e. not a transfer), we will also create (only // if authorized) a trust line or MPToken as needed, in doApply(). @@ -144,7 +191,9 @@ VaultWithdraw::doApply() assetsWithdrawn = *maybeAssets; } else + { return tefINTERNAL; // LCOV_EXCL_LINE + } } catch (std::overflow_error const&) { @@ -230,7 +279,7 @@ VaultWithdraw::doApply() associateAsset(*vault, vaultAsset); return doWithdraw( - view(), ctx_.tx, account_, dstAcct, vaultAccount, mPriorBalance, assetsWithdrawn, j_); + view(), ctx_.tx, account_, dstAcct, vaultAccount, preFeeBalance_, assetsWithdrawn, j_); } } // namespace xrpl diff --git a/src/test/README.md b/src/test/README.md index b012607f58..179d75941b 100644 --- a/src/test/README.md +++ b/src/test/README.md @@ -2,10 +2,10 @@ ## Running Tests -Unit tests are bundled in the `rippled` executable and can be executed using the +Unit tests are bundled in the `xrpld` executable and can be executed using the `--unittest` parameter. Without any arguments to this option, all non-manual unit tests will be executed. If you want to run one or more manual tests, you -must specify it by suite or full-name (e.g. `ripple.app.NoRippleCheckLimits` or +must specify it by suite or full-name (e.g. `xrpl.app.NoRippleCheckLimits` or just `NoRippleCheckLimits`). More than one suite or group of suites can be specified as a comma separated diff --git a/src/test/app/AMMCalc_test.cpp b/src/test/app/AMMCalc_test.cpp index 690329d2f3..021cf4bf46 100644 --- a/src/test/app/AMMCalc_test.cpp +++ b/src/test/app/AMMCalc_test.cpp @@ -1,7 +1,7 @@ #include +#include #include -#include #include @@ -37,16 +37,20 @@ class AMMCalc_test : public beast::unit_test::suite str = boost::regex_replace(str, boost::regex("^(A|O)[(]"), ""); boost::smatch match; // XXX(val))? - boost::regex rx("^([^(]+)[(]([^)]+)[)]([)])?$"); + boost::regex const rx("^([^(]+)[(]([^)]+)[)]([)])?$"); if (boost::regex_search(str, match, rx)) { - if (delimited) + if (delimited != nullptr) *delimited = (match[3] != ""); if (match[1] == "XRP") + { return XRP(std::stoll(match[2])); - // drops - else if (match[1] == "XRPA") + // drops + } + if (match[1] == "XRPA") + { return XRPAmount{std::stoll(match[2])}; + } return amountFromString(gw[match[1]].asset(), match[2]); } return std::nullopt; @@ -61,14 +65,14 @@ class AMMCalc_test : public beast::unit_test::suite str = boost::regex_replace(str, boost::regex("^T[(]"), ""); // XXX(rate))? boost::smatch match; - boost::regex rx("^([^(]+)[(]([^)]+)[)]([)])?$"); + boost::regex const rx("^([^(]+)[(]([^)]+)[)]([)])?$"); if (boost::regex_search(str, match, rx)) { std::string const currency = match[1]; // input is rate * 100, no fraction - std::uint32_t rate = 10'000'000 * std::stoi(match[2].str()); + std::uint32_t const rate = 10'000'000 * std::stoi(match[2].str()); // true if delimited - ) - return {{currency, rate, match[3] != "" ? true : false}}; + return {{currency, rate, match[3] != ""}}; } return std::nullopt; } @@ -90,7 +94,7 @@ class AMMCalc_test : public beast::unit_test::suite if (p == end_) return std::nullopt; std::string const s = *p; - bool const amm = s[0] == 'O' ? false : true; + bool const amm = s[0] != 'O'; auto const a1 = getAmt(p++); if (!a1 || p == end_) return std::nullopt; @@ -120,7 +124,9 @@ class AMMCalc_test : public beast::unit_test::suite break; } else + { return std::nullopt; + } } return rates; } @@ -155,7 +161,7 @@ class AMMCalc_test : public beast::unit_test::suite return {{pairs, *swap, *rate, fee}}; } - std::string + static std::string toString(STAmount const& a) { std::stringstream str; @@ -163,7 +169,7 @@ class AMMCalc_test : public beast::unit_test::suite return str.str(); } - STAmount + static STAmount mulratio(STAmount const& amt, std::uint32_t a, std::uint32_t b, bool round) { if (a == b) @@ -173,7 +179,7 @@ class AMMCalc_test : public beast::unit_test::suite return toSTAmount(mulRatio(amt.iou(), a, b, round), amt.issue()); } - void + static void swapOut(swapargs const& args) { auto const vp = std::get(args); @@ -236,7 +242,7 @@ class AMMCalc_test : public beast::unit_test::suite std::cout << "in: " << toString(resultIn) << " out: " << toString(resultOut) << std::endl; } - void + static void swapIn(swapargs const& args) { auto const vp = std::get(args); @@ -304,7 +310,7 @@ class AMMCalc_test : public beast::unit_test::suite { using namespace jtx; auto const a = arg(); - boost::regex re(","); + boost::regex const re(","); token_iter p(a.begin(), a.end(), re, -1); // Token is denoted as CUR(xxx), where CUR is the currency code // and xxx is the amount, for instance: XRP(100) or USD(11.5) @@ -385,7 +391,7 @@ class AMMCalc_test : public beast::unit_test::suite // 10 is AMM trading fee else if (*p == "changespq") { - Env env(*this); + Env const env(*this); if (auto const pool = getAmounts(++p)) { if (auto const offer = getAmounts(p)) @@ -398,13 +404,17 @@ class AMMCalc_test : public beast::unit_test::suite env.current()->rules(), beast::Journal(beast::Journal::getNullSink())); ammOffer) + { std::cout << "amm offer: " << toString(ammOffer->in) << " " << toString(ammOffer->out) << "\nnew pool: " << toString(pool->first.in + ammOffer->in) << " " << toString(pool->first.out - ammOffer->out) << std::endl; + } else + { std::cout << "can't change the pool's SP quality" << std::endl; + } return true; } } diff --git a/src/test/app/AMMClawback_test.cpp b/src/test/app/AMMClawback_test.cpp index 3034c628f4..a0def59c92 100644 --- a/src/test/app/AMMClawback_test.cpp +++ b/src/test/app/AMMClawback_test.cpp @@ -2,8 +2,8 @@ #include #include +#include #include -#include namespace xrpl { namespace test { @@ -18,8 +18,8 @@ class AMMClawback_test : public beast::unit_test::suite // Test if holder does not exist. { Env env(*this); - Account gw{"gateway"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; env.fund(XRP(100000), gw, alice); env.close(); @@ -32,7 +32,7 @@ class AMMClawback_test : public beast::unit_test::suite env.trust(USD(10000), alice); env(pay(gw, alice, USD(100))); - AMM amm(env, alice, XRP(100), USD(100)); + AMM const amm(env, alice, XRP(100), USD(100)); env.close(); env(amm::ammClawback(gw, Account("unknown"), USD, XRP, std::nullopt), @@ -44,7 +44,7 @@ class AMMClawback_test : public beast::unit_test::suite { Env env(*this); Account gw{"gateway"}; - Account alice{"alice"}; + Account const alice{"alice"}; env.fund(XRP(100000), gw, alice); env.close(); @@ -75,8 +75,8 @@ class AMMClawback_test : public beast::unit_test::suite // return temMALFORMED error. { Env env(*this); - Account gw{"gateway"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; env.fund(XRP(10000), gw, alice); env.close(); @@ -91,7 +91,7 @@ class AMMClawback_test : public beast::unit_test::suite env(pay(gw, alice, USD(100))); env.close(); - AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); + AMM const amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); // Issuer can not clawback from himself. env(amm::ammClawback(gw, gw, USD, XRP, std::nullopt), ter(temMALFORMED)); @@ -103,8 +103,8 @@ class AMMClawback_test : public beast::unit_test::suite // Test if the Asset field matches the Account field. { Env env(*this); - Account gw{"gateway"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; env.fund(XRP(10000), gw, alice); env.close(); @@ -119,7 +119,7 @@ class AMMClawback_test : public beast::unit_test::suite env(pay(gw, alice, USD(100))); env.close(); - AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); + AMM const amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); // The Asset's issuer field is alice, while the Account field is gw. // This should return temMALFORMED because they do not match. @@ -131,8 +131,8 @@ class AMMClawback_test : public beast::unit_test::suite // Test if the Amount field matches the Asset field. { Env env(*this); - Account gw{"gateway"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; env.fund(XRP(10000), gw, alice); env.close(); @@ -147,7 +147,7 @@ class AMMClawback_test : public beast::unit_test::suite env(pay(gw, alice, USD(100))); env.close(); - AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); + AMM const amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); // The Asset's issuer subfield is gw account and Amount's issuer // subfield is alice account. Return temBAD_AMOUNT because @@ -160,8 +160,8 @@ class AMMClawback_test : public beast::unit_test::suite // Test if the Amount is invalid, which is less than zero. { Env env(*this); - Account gw{"gateway"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; env.fund(XRP(10000), gw, alice); env.close(); @@ -176,7 +176,7 @@ class AMMClawback_test : public beast::unit_test::suite env(pay(gw, alice, USD(100))); env.close(); - AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); + AMM const amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); // Return temBAD_AMOUNT if the Amount value is less than 0. env(amm::ammClawback( @@ -193,8 +193,8 @@ class AMMClawback_test : public beast::unit_test::suite // transaction is prohibited. { Env env(*this); - Account gw{"gateway"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; env.fund(XRP(10000), gw, alice); env.close(); @@ -207,7 +207,7 @@ class AMMClawback_test : public beast::unit_test::suite env.require(balance(gw, alice["USD"](-100))); // gw creates AMM pool of XRP/USD. - AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); + AMM const amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); // If asfAllowTrustLineClawback is not set, the issuer is not // allowed to send the AMMClawback transaction. @@ -217,8 +217,8 @@ class AMMClawback_test : public beast::unit_test::suite // Test invalid flag. { Env env(*this); - Account gw{"gateway"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; env.fund(XRP(10000), gw, alice); env.close(); @@ -233,7 +233,7 @@ class AMMClawback_test : public beast::unit_test::suite env(pay(gw, alice, USD(100))); env.close(); - AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); + AMM const amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); // Return temINVALID_FLAG when providing invalid flag. env(amm::ammClawback(gw, alice, USD, XRP, std::nullopt), @@ -245,8 +245,8 @@ class AMMClawback_test : public beast::unit_test::suite // are not issued by the same issuer. { Env env(*this); - Account gw{"gateway"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; env.fund(XRP(10000), gw, alice); env.close(); @@ -262,7 +262,7 @@ class AMMClawback_test : public beast::unit_test::suite env.close(); // gw creates AMM pool of XRP/USD. - AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); + AMM const amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); // Return temINVALID_FLAG because the issuer set tfClawTwoAssets, // but the issuer only issues USD in the pool. The issuer is not @@ -276,8 +276,8 @@ class AMMClawback_test : public beast::unit_test::suite // Test clawing back XRP is being prohibited. { Env env(*this); - Account gw{"gateway"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; env.fund(XRP(1000000), gw, alice); env.close(); @@ -293,7 +293,7 @@ class AMMClawback_test : public beast::unit_test::suite env.close(); // Alice creates AMM pool of XRP/USD. - AMM amm(env, alice, XRP(1000), USD(2000), ter(tesSUCCESS)); + AMM const amm(env, alice, XRP(1000), USD(2000), ter(tesSUCCESS)); env.close(); // Clawback XRP is prohibited. @@ -309,8 +309,8 @@ class AMMClawback_test : public beast::unit_test::suite if (!features[featureAMMClawback]) { Env env(*this, features); - Account gw{"gateway"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; env.fund(XRP(1000000), gw, alice); env.close(); @@ -342,9 +342,9 @@ class AMMClawback_test : public beast::unit_test::suite // issuer. Claw back USD, and EUR goes back to the holder. { Env env(*this, features); - Account gw{"gateway"}; - Account gw2{"gateway2"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const gw2{"gateway2"}; + Account const alice{"alice"}; env.fund(XRP(1000000), gw, gw2, alice); env.close(); @@ -370,7 +370,7 @@ class AMMClawback_test : public beast::unit_test::suite env.require(balance(alice, EUR(3000))); // Alice creates AMM pool of EUR/USD. - AMM amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS)); + AMM const amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS)); env.close(); BEAST_EXPECT( @@ -422,8 +422,8 @@ class AMMClawback_test : public beast::unit_test::suite // to the holder. { Env env(*this, features); - Account gw{"gateway"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; env.fund(XRP(1000000), gw, alice); env.close(); @@ -441,7 +441,7 @@ class AMMClawback_test : public beast::unit_test::suite env.require(balance(alice, USD(3000))); // Alice creates AMM pool of XRP/USD. - AMM amm(env, alice, XRP(1000), USD(2000), ter(tesSUCCESS)); + AMM const amm(env, alice, XRP(1000), USD(2000), ter(tesSUCCESS)); env.close(); BEAST_EXPECT(amm.expectBalances(USD(2000), XRP(1000), IOUAmount{1414213562373095, -9})); @@ -501,9 +501,9 @@ class AMMClawback_test : public beast::unit_test::suite // balance in AMM pool. { Env env(*this, features); - Account gw{"gateway"}; - Account gw2{"gateway2"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const gw2{"gateway2"}; + Account const alice{"alice"}; env.fund(XRP(1000000), gw, gw2, alice); env.close(); @@ -527,15 +527,19 @@ class AMMClawback_test : public beast::unit_test::suite env.require(balance(alice, EUR(6000))); // Alice creates AMM pool of EUR/USD - AMM amm(env, alice, EUR(5000), USD(4000), ter(tesSUCCESS)); + AMM const amm(env, alice, EUR(5000), USD(4000), ter(tesSUCCESS)); env.close(); if (!features[fixAMMv1_3]) + { BEAST_EXPECT( amm.expectBalances(USD(4000), EUR(5000), IOUAmount{4472135954999580, -12})); + } else + { BEAST_EXPECT( amm.expectBalances(USD(4000), EUR(5000), IOUAmount{4472135954999579, -12})); + } // gw clawback 1000 USD from the AMM pool env(amm::ammClawback(gw, alice, USD, EUR, USD(1000)), ter(tesSUCCESS)); @@ -554,17 +558,25 @@ class AMMClawback_test : public beast::unit_test::suite // 1000 USD and 1250 EUR was withdrawn from the AMM pool, so the // current balance is 3000 USD and 3750 EUR. if (!features[fixAMMv1_3]) + { BEAST_EXPECT( amm.expectBalances(USD(3000), EUR(3750), IOUAmount{3354101966249685, -12})); + } else + { BEAST_EXPECT( amm.expectBalances(USD(3000), EUR(3750), IOUAmount{3354101966249684, -12})); + } // Alice has 3/4 of its initial lptokens Left. if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{3354101966249685, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{3354101966249684, -12})); + } // gw clawback another 500 USD from the AMM pool. env(amm::ammClawback(gw, alice, USD, EUR, USD(500)), ter(tesSUCCESS)); @@ -575,19 +587,27 @@ class AMMClawback_test : public beast::unit_test::suite env.require(balance(alice, USD(2000))); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(2500000000000001), -12}, STAmount{EUR, UINT64_C(3125000000000001), -12}, IOUAmount{2795084971874738, -12})); + } else + { BEAST_EXPECT( amm.expectBalances(USD(2500), EUR(3125), IOUAmount{2795084971874737, -12})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT( env.balance(alice, EUR) == STAmount(EUR, UINT64_C(2874999999999999), -12)); + } else + { BEAST_EXPECT(env.balance(alice, EUR) == EUR(2875)); + } // gw clawback small amount, 1 USD. env(amm::ammClawback(gw, alice, USD, EUR, USD(1)), ter(tesSUCCESS)); @@ -597,27 +617,39 @@ class AMMClawback_test : public beast::unit_test::suite env.require(balance(alice, USD(2000))); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(2499000000000002), -12}, STAmount{EUR, UINT64_C(3123750000000002), -12}, IOUAmount{2793966937885989, -12})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT( amm.expectBalances(USD(2499), EUR(3123.75), IOUAmount{2793966937885987, -12})); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(2499000000000001), -12}, STAmount{EUR, UINT64_C(3123750000000001), -12}, IOUAmount{2793966937885988, -12})); + } if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT( env.balance(alice, EUR) == STAmount(EUR, UINT64_C(2876'249999999998), -12)); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(env.balance(alice, EUR) == EUR(2876.25)); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT( env.balance(alice, EUR) == STAmount(EUR, UINT64_C(2876'249999999999), -12)); + } // gw clawback 4000 USD, exceeding the current balance. We // will clawback all. @@ -640,8 +672,8 @@ class AMMClawback_test : public beast::unit_test::suite // creates the AMM pool EUR/XRP. { Env env(*this, features); - Account gw{"gateway"}; - Account gw2{"gateway2"}; + Account const gw{"gateway"}; + Account const gw2{"gateway2"}; Account alice{"alice"}; Account bob{"bob"}; env.fund(XRP(1000000), gw, gw2, alice, bob); @@ -685,27 +717,39 @@ class AMMClawback_test : public beast::unit_test::suite // gw2 creates AMM pool of XRP/EUR, alice and bob deposit XRP/EUR. AMM amm2(env, gw2, XRP(3000), EUR(1000), ter(tesSUCCESS)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT( amm2.expectBalances(EUR(1000), XRP(3000), IOUAmount{1732050807568878, -9})); + } else + { BEAST_EXPECT( amm2.expectBalances(EUR(1000), XRP(3000), IOUAmount{1732050807568877, -9})); + } amm2.deposit(alice, EUR(1000), XRP(3000)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT( amm2.expectBalances(EUR(2000), XRP(6000), IOUAmount{3464101615137756, -9})); + } else + { BEAST_EXPECT( amm2.expectBalances(EUR(2000), XRP(6000), IOUAmount{3464101615137754, -9})); + } amm2.deposit(bob, EUR(1000), XRP(3000)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT( amm2.expectBalances(EUR(3000), XRP(9000), IOUAmount{5196152422706634, -9})); + } else + { BEAST_EXPECT( amm2.expectBalances(EUR(3000), XRP(9000), IOUAmount{5196152422706631, -9})); + } env.close(); auto aliceXrpBalance = env.balance(alice, XRP); @@ -725,28 +769,44 @@ class AMMClawback_test : public beast::unit_test::suite // Alice gets 1000 XRP back. if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT( expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(1000) - XRPAmount(1))); + } else + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(1000))); + } aliceXrpBalance = env.balance(alice, XRP); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT( amm.expectBalances(USD(2500), XRP(5000), IOUAmount{3535533905932738, -9})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT( amm.expectBalances(USD(2500), XRP(5000), IOUAmount{3535533905932737, -9})); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectBalances( USD(2500), XRPAmount(5000000001), IOUAmount{3'535'533'905932738, -9})); + } if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{7071067811865480, -10})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{7071067811865474, -10})); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{707106781186548, -9})); + } BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{1414213562373095, -9})); @@ -762,32 +822,50 @@ class AMMClawback_test : public beast::unit_test::suite bobXrpBalance = env.balance(bob, XRP); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(2490000000000001), -12}, XRP(4980), IOUAmount{3521391770309008, -9})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT( amm.expectBalances(USD(2'490), XRP(4980), IOUAmount{3521391770309006, -9})); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(2490000000000001), -12}, XRPAmount(4980000001), IOUAmount{3521391'770309008, -9})); + } if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{7071067811865480, -10})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{7071067811865474, -10})); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{707106781186548, -9})); + } if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{1400071426749365, -9})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{1400071426749364, -9})); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{1400071426749365, -9})); + } // gw2 clawback 200 EUR from amm2. env(amm::ammClawback(gw2, alice, EUR, XRP, EUR(200)), ter(tesSUCCESS)); @@ -797,32 +875,52 @@ class AMMClawback_test : public beast::unit_test::suite env.require(balance(bob, EUR(3000))); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(600))); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(600))); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT( expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(600) - XRPAmount{1})); + } aliceXrpBalance = env.balance(alice, XRP); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT( amm2.expectBalances(EUR(2800), XRP(8400), IOUAmount{4849742261192859, -9})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT( amm2.expectBalances(EUR(2800), XRP(8400), IOUAmount{4849742261192856, -9})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm2.expectBalances( EUR(2800), XRPAmount(8400000001), IOUAmount{4849742261192856, -9})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm2.expectLPTokens(alice, IOUAmount{1385640646055103, -9})); + } else + { BEAST_EXPECT(amm2.expectLPTokens(alice, IOUAmount{1385640646055102, -9})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm2.expectLPTokens(bob, IOUAmount{1732050807568878, -9})); + } else + { BEAST_EXPECT(amm2.expectLPTokens(bob, IOUAmount{1732050807568877, -9})); + } // gw claw back 1000 USD from alice in amm, which exceeds alice's // balance. This will clawback all the remaining LP tokens of alice @@ -835,35 +933,53 @@ class AMMClawback_test : public beast::unit_test::suite // Alice gets 1000 XRP back. if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(1000))); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT( expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(1000) - XRPAmount{1})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(1000))); + } aliceXrpBalance = env.balance(alice, XRP); BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(0))); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{1400071426749365, -9})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{1400071426749364, -9})); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{1400071426749365, -9})); + } if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(1990000000000001), -12}, XRP(3980), IOUAmount{2814284989122460, -9})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( USD(1'990), XRPAmount{3'980'000'001}, IOUAmount{2814284989122459, -9})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(1990000000000001), -12}, XRPAmount{3'980'000'001}, IOUAmount{2814284989122460, -9})); + } // gw clawback 1000 USD from bob in amm, which also exceeds bob's // balance in amm. All bob's lptoken in amm will be consumed, which @@ -903,14 +1019,20 @@ class AMMClawback_test : public beast::unit_test::suite BEAST_EXPECT(amm2.expectLPTokens(alice, IOUAmount(0))); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT( amm2.expectBalances(EUR(2000), XRP(6000), IOUAmount{3464101615137756, -9})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT( amm2.expectBalances(EUR(2000), XRP(6000), IOUAmount{3464101615137754, -9})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm2.expectBalances( EUR(2000), XRPAmount(6000000001), IOUAmount{3464101615137754, -9})); + } // gw2 claw back 2000 EUR from bob in amm2, which exceeds bob's // balance. All bob's lptokens will be consumed, which corresponds @@ -933,14 +1055,20 @@ class AMMClawback_test : public beast::unit_test::suite BEAST_EXPECT(amm2.expectLPTokens(bob, IOUAmount(0))); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT( amm2.expectBalances(EUR(1000), XRP(3000), IOUAmount{1732050807568878, -9})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT( amm2.expectBalances(EUR(1000), XRP(3000), IOUAmount{1732050807568877, -9})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm2.expectBalances( EUR(1000), XRPAmount(3000000001), IOUAmount{1732050807568877, -9})); + } } } @@ -954,9 +1082,9 @@ class AMMClawback_test : public beast::unit_test::suite // issuer. Claw back all the USD for different users. { Env env(*this, features); - Account gw{"gateway"}; - Account gw2{"gateway2"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const gw2{"gateway2"}; + Account const alice{"alice"}; Account bob{"bob"}; Account carol{"carol"}; env.fund(XRP(1000000), gw, gw2, alice, bob, carol); @@ -999,38 +1127,62 @@ class AMMClawback_test : public beast::unit_test::suite env.close(); if (!features[fixAMMv1_3]) + { BEAST_EXPECT( amm.expectBalances(USD(4000), EUR(5000), IOUAmount{4472135954999580, -12})); + } else + { BEAST_EXPECT( amm.expectBalances(USD(4000), EUR(5000), IOUAmount{4472135954999579, -12})); + } amm.deposit(bob, USD(2000), EUR(2500)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT( amm.expectBalances(USD(6000), EUR(7500), IOUAmount{6708203932499370, -12})); + } else + { BEAST_EXPECT( amm.expectBalances(USD(6000), EUR(7500), IOUAmount{6708203932499368, -12})); + } amm.deposit(carol, USD(1000), EUR(1250)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT( amm.expectBalances(USD(7000), EUR(8750), IOUAmount{7826237921249265, -12})); + } else + { BEAST_EXPECT( amm.expectBalances(USD(7000), EUR(8750), IOUAmount{7826237921249262, -12})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4472135954999580, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4472135954999579, -12})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{2236067977499790, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{2236067977499789, -12})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount{1118033988749895, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount{1118033988749894, -12})); + } env.require(balance(alice, USD(2000))); env.require(balance(alice, EUR(1000))); @@ -1044,25 +1196,37 @@ class AMMClawback_test : public beast::unit_test::suite env.close(); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(4999999999999999), -12}, STAmount{EUR, UINT64_C(6249999999999999), -12}, IOUAmount{5590169943749475, -12})); + } else + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(5000000000000001), -12}, STAmount{EUR, UINT64_C(6250000000000001), -12}, IOUAmount{5590169943749473, -12})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4472135954999580, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4472135954999579, -12})); + } BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(0))); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount{1118033988749895, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount{1118033988749894, -12})); + } // Bob will get 2500 EUR back. env.require(balance(alice, USD(2000))); @@ -1070,11 +1234,15 @@ class AMMClawback_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(bob, USD) == STAmount(USD, UINT64_C(3000000000000000), -12)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT( env.balance(bob, EUR) == STAmount(EUR, UINT64_C(5000000000000001), -12)); + } else + { BEAST_EXPECT( env.balance(bob, EUR) == STAmount(EUR, UINT64_C(4999999999999999), -12)); + } env.require(balance(carol, USD(3000))); env.require(balance(carol, EUR(2750))); @@ -1082,20 +1250,28 @@ class AMMClawback_test : public beast::unit_test::suite env(amm::ammClawback(gw2, carol, EUR, USD, std::nullopt), ter(tesSUCCESS)); env.close(); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(3999999999999999), -12}, STAmount{EUR, UINT64_C(4999999999999999), -12}, IOUAmount{4472135954999580, -12})); + } else + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(4000000000000001), -12}, STAmount{EUR, UINT64_C(5000000000000002), -12}, IOUAmount{4472135954999579, -12})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4472135954999580, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4472135954999579, -12})); + } BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(0))); BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount(0))); @@ -1112,7 +1288,7 @@ class AMMClawback_test : public beast::unit_test::suite // different users. { Env env(*this, features); - Account gw{"gateway"}; + Account const gw{"gateway"}; Account alice{"alice"}; Account bob{"bob"}; env.fund(XRP(1000000), gw, alice, bob); @@ -1134,25 +1310,37 @@ class AMMClawback_test : public beast::unit_test::suite // gw creates AMM pool of XRP/USD, alice and bob deposit XRP/USD. AMM amm(env, gw, XRP(2000), USD(10000), ter(tesSUCCESS)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT( amm.expectBalances(USD(10000), XRP(2000), IOUAmount{4472135954999580, -9})); + } else + { BEAST_EXPECT( amm.expectBalances(USD(10000), XRP(2000), IOUAmount{4472135954999579, -9})); + } amm.deposit(alice, USD(1000), XRP(200)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT( amm.expectBalances(USD(11000), XRP(2200), IOUAmount{4919349550499538, -9})); + } else + { BEAST_EXPECT( amm.expectBalances(USD(11000), XRP(2200), IOUAmount{4919349550499536, -9})); + } amm.deposit(bob, USD(2000), XRP(400)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT( amm.expectBalances(USD(13000), XRP(2600), IOUAmount{5813776741499453, -9})); + } else + { BEAST_EXPECT( amm.expectBalances(USD(13000), XRP(2600), IOUAmount{5813776741499451, -9})); + } env.close(); auto aliceXrpBalance = env.balance(alice, XRP); @@ -1162,27 +1350,39 @@ class AMMClawback_test : public beast::unit_test::suite env(amm::ammClawback(gw, alice, USD, XRP, std::nullopt), ter(tesSUCCESS)); env.close(); if (!features[fixAMMv1_3]) + { BEAST_EXPECT( amm.expectBalances(USD(12000), XRP(2400), IOUAmount{5366563145999495, -9})); + } else + { BEAST_EXPECT(amm.expectBalances( USD(12000), XRPAmount(2400000001), IOUAmount{5366563145999494, -9})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(200))); + } else + { BEAST_EXPECT( expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(200) - XRPAmount{1})); + } BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(0))); // gw clawback all bob's USD in amm. (2000 USD / 400 XRP) env(amm::ammClawback(gw, bob, USD, XRP, std::nullopt), ter(tesSUCCESS)); env.close(); if (!features[fixAMMv1_3]) + { BEAST_EXPECT( amm.expectBalances(USD(10000), XRP(2000), IOUAmount{4472135954999580, -9})); + } else + { BEAST_EXPECT(amm.expectBalances( USD(10000), XRPAmount(2000000001), IOUAmount{4472135954999579, -9})); + } BEAST_EXPECT(expectLedgerEntryRoot(env, bob, bobXrpBalance + XRP(400))); BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(0))); BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(0))); @@ -1200,8 +1400,8 @@ class AMMClawback_test : public beast::unit_test::suite // Test AMMClawback for USD/EUR pool. The assets are issued by different // issuer. Claw back all the USD for different users. Env env(*this, features); - Account gw{"gateway"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; Account bob{"bob"}; Account carol{"carol"}; env.fund(XRP(1000000), gw, alice, bob, carol); @@ -1237,9 +1437,13 @@ class AMMClawback_test : public beast::unit_test::suite amm.deposit(bob, USD(4000), EUR(1000)); BEAST_EXPECT(amm.expectBalances(USD(12000), EUR(3000), IOUAmount(6000))); if (!features[fixAMMv1_3]) + { amm.deposit(carol, USD(2000), EUR(500)); + } else + { amm.deposit(carol, USD(2000.25), EUR(500)); + } BEAST_EXPECT(amm.expectBalances(USD(14000), EUR(3500), IOUAmount(7000))); // gw clawback 1000 USD from carol. env(amm::ammClawback(gw, carol, USD, EUR, USD(1000)), ter(tesSUCCESS)); @@ -1254,10 +1458,14 @@ class AMMClawback_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(bob, USD) == USD(5000)); BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(env.balance(carol, USD) == USD(6000)); + } else + { BEAST_EXPECT( env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + } // 250 EUR goes back to carol. BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750)); @@ -1279,10 +1487,14 @@ class AMMClawback_test : public beast::unit_test::suite // 250 EUR did not go back to bob because tfClawTwoAssets is set. BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(env.balance(carol, USD) == USD(6000)); + } else + { BEAST_EXPECT( env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + } BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750)); // gw clawback all USD from alice and set tfClawTwoAssets. @@ -1300,10 +1512,14 @@ class AMMClawback_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(bob, USD) == USD(5000)); BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(env.balance(carol, USD) == USD(6000)); + } else + { BEAST_EXPECT( env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + } BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750)); } @@ -1318,9 +1534,9 @@ class AMMClawback_test : public beast::unit_test::suite // Test AMMClawback for USD/EUR pool. The assets are issued by different // issuer. Claw back all the USD for different users. Env env(*this, features); - Account gw{"gateway"}; - Account gw2{"gateway2"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const gw2{"gateway2"}; + Account const alice{"alice"}; Account bob{"bob"}; env.fund(XRP(1000000), gw, gw2, alice, bob); env.close(); @@ -1413,7 +1629,7 @@ class AMMClawback_test : public beast::unit_test::suite // gw and gw2 issues token for each other. Test AMMClawback from // each other. Env env(*this, features); - Account gw{"gateway"}; + Account const gw{"gateway"}; Account gw2{"gateway2"}; Account alice{"alice"}; env.fund(XRP(1000000), gw, gw2, alice); @@ -1460,17 +1676,25 @@ class AMMClawback_test : public beast::unit_test::suite env(amm::ammClawback(gw, gw2, USD, EUR, USD(1000)), ter(tesSUCCESS)); env.close(); if (!features[fixAMMv1_3] || !features[fixAMMClawbackRounding]) + { BEAST_EXPECT( amm.expectBalances(USD(5000), EUR(10000), IOUAmount{7071067811865475, -12})); + } else + { BEAST_EXPECT( amm.expectBalances(USD(5000), EUR(10000), IOUAmount{7071067811865474, -12})); + } BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{1414213562373095, -12})); if (!features[fixAMMv1_3] || !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{1414213562373095, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{1414213562373094, -12})); + } BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4242640687119285, -12})); BEAST_EXPECT(env.balance(alice, USD) == USD(2000)); @@ -1482,30 +1706,46 @@ class AMMClawback_test : public beast::unit_test::suite env(amm::ammClawback(gw2, gw, EUR, USD, EUR(1000)), ter(tesSUCCESS)); env.close(); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( USD(4500), STAmount(EUR, UINT64_C(9000000000000001), -12), IOUAmount{6363961030678928, -12})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT( amm.expectBalances(USD(4500), EUR(9000), IOUAmount{6363961030678928, -12})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( USD(4500), STAmount(EUR, UINT64_C(9000000000000001), -12), IOUAmount{6363961030678927, -12})); + } if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{7071067811865480, -13})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{7071067811865475, -13})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{7071067811865480, -13})); + } if (!features[fixAMMv1_3] || !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{1414213562373095, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{1414213562373094, -12})); + } BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4242640687119285, -12})); @@ -1518,30 +1758,46 @@ class AMMClawback_test : public beast::unit_test::suite env(amm::ammClawback(gw2, alice, EUR, USD, EUR(4000)), ter(tesSUCCESS)); env.close(); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( USD(2500), STAmount(EUR, UINT64_C(5000000000000001), -12), IOUAmount{3535533905932738, -12})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT( amm.expectBalances(USD(2500), EUR(5000), IOUAmount{3535533905932738, -12})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( USD(2500), STAmount(EUR, UINT64_C(5000000000000001), -12), IOUAmount{3535533905932737, -12})); + } if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{7071067811865480, -13})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{7071067811865475, -13})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{7071067811865480, -13})); + } if (!features[fixAMMv1_3] || !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{1414213562373095, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{1414213562373094, -12})); + } BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{1414213562373095, -12})); BEAST_EXPECT(env.balance(alice, USD) == USD(4000)); @@ -1559,8 +1815,8 @@ class AMMClawback_test : public beast::unit_test::suite using namespace jtx; Env env(*this, features); - Account gw{"gateway"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; env.fund(XRP(1000000), gw, alice); env.close(); @@ -1573,7 +1829,7 @@ class AMMClawback_test : public beast::unit_test::suite env.trust(USD(100000), alice); env(pay(gw, alice, USD(5000))); - AMM amm(env, gw, USD(1000), XRP(2000), ter(tesSUCCESS)); + AMM const amm(env, gw, USD(1000), XRP(2000), ter(tesSUCCESS)); env.close(); // Alice did not deposit in the amm pool. So AMMClawback from Alice @@ -1590,9 +1846,9 @@ class AMMClawback_test : public beast::unit_test::suite // test individually frozen trustline. { Env env(*this, features); - Account gw{"gateway"}; - Account gw2{"gateway2"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const gw2{"gateway2"}; + Account const alice{"alice"}; env.fund(XRP(1000000), gw, gw2, alice); env.close(); @@ -1616,7 +1872,7 @@ class AMMClawback_test : public beast::unit_test::suite env.require(balance(alice, EUR(3000))); // Alice creates AMM pool of EUR/USD. - AMM amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS)); + AMM const amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS)); env.close(); BEAST_EXPECT( @@ -1654,9 +1910,9 @@ class AMMClawback_test : public beast::unit_test::suite // test individually frozen trustline of both USD and EUR currency. { Env env(*this, features); - Account gw{"gateway"}; - Account gw2{"gateway2"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const gw2{"gateway2"}; + Account const alice{"alice"}; env.fund(XRP(1000000), gw, gw2, alice); env.close(); @@ -1680,7 +1936,7 @@ class AMMClawback_test : public beast::unit_test::suite env.require(balance(alice, EUR(3000))); // Alice creates AMM pool of EUR/USD. - AMM amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS)); + AMM const amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS)); env.close(); BEAST_EXPECT( @@ -1704,9 +1960,9 @@ class AMMClawback_test : public beast::unit_test::suite // test gw global freeze. { Env env(*this, features); - Account gw{"gateway"}; - Account gw2{"gateway2"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const gw2{"gateway2"}; + Account const alice{"alice"}; env.fund(XRP(1000000), gw, gw2, alice); env.close(); @@ -1730,7 +1986,7 @@ class AMMClawback_test : public beast::unit_test::suite env.require(balance(alice, EUR(3000))); // Alice creates AMM pool of EUR/USD. - AMM amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS)); + AMM const amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS)); env.close(); BEAST_EXPECT( @@ -1754,8 +2010,8 @@ class AMMClawback_test : public beast::unit_test::suite // global freeze. { Env env(*this, features); - Account gw{"gateway"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; Account bob{"bob"}; Account carol{"carol"}; env.fund(XRP(1000000), gw, alice, bob, carol); @@ -1791,9 +2047,13 @@ class AMMClawback_test : public beast::unit_test::suite amm.deposit(bob, USD(4000), EUR(1000)); BEAST_EXPECT(amm.expectBalances(USD(12000), EUR(3000), IOUAmount(6000))); if (!features[fixAMMv1_3]) + { amm.deposit(carol, USD(2000), EUR(500)); + } else + { amm.deposit(carol, USD(2000.25), EUR(500)); + } BEAST_EXPECT(amm.expectBalances(USD(14000), EUR(3500), IOUAmount(7000))); // global freeze @@ -1813,10 +2073,14 @@ class AMMClawback_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(bob, USD) == USD(5000)); BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(env.balance(carol, USD) == USD(6000)); + } else + { BEAST_EXPECT( env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + } // 250 EUR goes back to carol. BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750)); @@ -1838,10 +2102,14 @@ class AMMClawback_test : public beast::unit_test::suite // 250 EUR did not go back to bob because tfClawTwoAssets is set. BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(env.balance(carol, USD) == USD(6000)); + } else + { BEAST_EXPECT( env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + } BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750)); // gw clawback all USD from alice and set tfClawTwoAssets. @@ -1859,10 +2127,14 @@ class AMMClawback_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(bob, USD) == USD(5000)); BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(env.balance(carol, USD) == USD(6000)); + } else + { BEAST_EXPECT( env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + } BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750)); } } @@ -1877,7 +2149,7 @@ class AMMClawback_test : public beast::unit_test::suite // Test AMMClawback for USD/XRP pool. Claw back USD, and XRP goes back // to the holder. Env env(*this, features, std::make_unique(&logs)); - Account gw{"gateway"}; + Account const gw{"gateway"}; Account alice{"alice"}; env.fund(XRP(1000000000), gw, alice); env.close(); @@ -1911,16 +2183,24 @@ class AMMClawback_test : public beast::unit_test::suite env.close(); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectBalances( STAmount(USD, UINT64_C(5656854249492380), -13), XRP(70.710678), IOUAmount(200000))); + } else + { BEAST_EXPECT(amm.expectBalances( STAmount(USD, UINT64_C(565'685424949238), -12), XRP(70.710679), IOUAmount(200000))); + } BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(0))); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(29.289322))); + } else + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(29.289321))); + } } void @@ -2077,7 +2357,7 @@ class AMMClawback_test : public beast::unit_test::suite Account gw{"gateway"}, alice{"alice"}, bob{"bob"}; auto const USD = setupAccounts(env, gw, alice, bob); - Account gw2{"gateway2"}; + Account const gw2{"gateway2"}; env.fund(XRP(100000), gw2); env.close(); auto const EUR = gw2["EUR"]; diff --git a/src/test/app/AMMExtended_test.cpp b/src/test/app/AMMExtended_test.cpp index 14be260822..281ceafd19 100644 --- a/src/test/app/AMMExtended_test.cpp +++ b/src/test/app/AMMExtended_test.cpp @@ -5,15 +5,14 @@ #include #include -#include - #include +#include #include #include +#include #include #include #include -#include #include #include @@ -62,9 +61,9 @@ private: env(offer(carol, XRP(50), USD(50))); // Good quality path - AMM ammCarol(env, carol, BTC(1'000), USD(100'100)); + AMM const ammCarol(env, carol, BTC(1'000), USD(100'100)); - PathSet paths(Path(XRP, USD), Path(USD)); + PathSet const paths(Path(XRP, USD), Path(USD)); env(pay(alice, bob, USD(100)), json(paths.json()), @@ -115,7 +114,7 @@ private: env(pay(gw2, bob, USD2(50))); env.close(); - AMM ammDan(env, dan, XRP(10'000), USD1(10'000)); + AMM const ammDan(env, dan, XRP(10'000), USD1(10'000)); env(pay(alice, carol, USD2(50)), path(~USD1, bob), @@ -146,7 +145,7 @@ private: env(pay(gw2, bob, USD2(50))); env.close(); - AMM ammDan(env, dan, XRP(10'000), USD1(10'050)); + AMM const ammDan(env, dan, XRP(10'000), USD1(10'050)); env(pay(alice, carol, USD2(50)), path(~USD1, bob), @@ -270,7 +269,7 @@ private: fund(env, gw, {bob, alice}, XRP(300'000), {USD(100)}, Fund::All); - AMM ammAlice(env, alice, XRP(150'000), USD(50)); + AMM const ammAlice(env, alice, XRP(150'000), USD(50)); // Existing offer pays better than this wants. // Partially consume existing offer. @@ -303,7 +302,7 @@ private: env(pay(gw, alice, alice["USD"](500))); - AMM ammAlice(env, alice, XRP(150'000), USD(51)); + AMM const ammAlice(env, alice, XRP(150'000), USD(51)); env(offer(bob, USD(1), XRP(3'000))); BEAST_EXPECT(ammAlice.expectBalances(XRP(153'000), USD(50), ammAlice.tokens())); @@ -335,7 +334,7 @@ private: env.require(owners(alice, 1), owners(bob, 1)); env(pay(gw, alice, alice["USD"](100))); - AMM ammBob(env, bob, USD(200), XRP(1'500)); + AMM const ammBob(env, bob, USD(200), XRP(1'500)); env(pay(alice, alice, XRP(500)), sendmax(USD(100))); @@ -460,7 +459,7 @@ private: env(pay(gw2, dan, dan["EUR"](400))); env.close(); - AMM ammCarol(env, carol, USD1(5'000), XRP(50'000)); + AMM const ammCarol(env, carol, USD1(5'000), XRP(50'000)); env(offer(dan, XRP(500), EUR1(50))); env.close(); @@ -514,7 +513,7 @@ private: env(pay(gw1, bob, bob["USD"](1'200))); - AMM ammBob(env, bob, XRP(1'000), USD1(1'200)); + AMM const ammBob(env, bob, XRP(1'000), USD1(1'200)); // Alice has 350 fees - a reserve of 50 = 250 reserve = 100 available. // Ask for more than available to prove reserve works. env(offer(alice, USD1(200), XRP(200))); @@ -549,7 +548,7 @@ private: env(pay(gw, bob, USD(1))); env(pay(gw, alice, USD(200))); - AMM ammAlice(env, alice, USD(150), XRP(150'100)); + AMM const ammAlice(env, alice, USD(150), XRP(150'100)); env(offer(bob, XRP(100), USD(0.1))); BEAST_EXPECT(ammAlice.expectBalances(USD(150.1), XRP(150'000), ammAlice.tokens())); @@ -602,7 +601,7 @@ private: env(pay(gw, bob, bob["USD"](2'200))); - AMM ammBob(env, bob, XRP(1'000), USD(2'200)); + AMM const ammBob(env, bob, XRP(1'000), USD(2'200)); // Alice has 350 fees - a reserve of 50 = 250 reserve = 100 available. // Ask for more than available to prove reserve works. // Taker pays 100 USD for 100 XRP. @@ -630,7 +629,7 @@ private: auto const starting_xrp = XRP(100.1) + reserve(env, 1) + env.current()->fees().base * 2; fund(env, gw, {alice, bob}, starting_xrp, {XTS(100), XXX(100)}, Fund::All); - AMM ammAlice(env, alice, XTS(100), XXX(100)); + AMM const ammAlice(env, alice, XTS(100), XXX(100)); Json::Value payment; payment[jss::secret] = toBase58(generateSeed("bob")); @@ -678,8 +677,8 @@ private: // o carol has EUR but wants USD. // Note that carol's offer must come last. If carol's offer is // placed before AMM is created, then autobridging will not occur. - AMM ammAlice(env, alice, XRP(10'000), USD(10'100)); - AMM ammBob(env, bob, EUR(10'000), XRP(10'100)); + AMM const ammAlice(env, alice, XRP(10'000), USD(10'100)); + AMM const ammBob(env, bob, EUR(10'000), XRP(10'100)); // Carol makes an offer that consumes AMM liquidity and // fully consumes Carol's offer. @@ -705,7 +704,7 @@ private: // Note that carol's offer must come last. If carol's offer is // placed before AMM and bob's offer are created, then autobridging // will not occur. - AMM ammAlice(env, alice, XRP(10'000), USD(10'100)); + AMM const ammAlice(env, alice, XRP(10'000), USD(10'100)); env(offer(bob, EUR(100), XRP(100))); env.close(); @@ -735,7 +734,7 @@ private: // autobridging will not occur. env(offer(alice, XRP(100), USD(100))); env.close(); - AMM ammBob(env, bob, EUR(10'000), XRP(10'100)); + AMM const ammBob(env, bob, EUR(10'000), XRP(10'100)); // Carol makes an offer that consumes AMM liquidity and // fully consumes Carol's offer. @@ -765,7 +764,7 @@ private: { Env env{*this, features}; fund(env, gw, {alice, bob}, {USD(20'000)}, Fund::All); - AMM ammBob(env, bob, XRP(20'000), USD(200)); + AMM const ammBob(env, bob, XRP(20'000), USD(200)); // alice submits a tfSell | tfFillOrKill offer that does not cross. env(offer(alice, USD(2.1), XRP(210), tfSell | tfFillOrKill), ter(killedCode)); @@ -775,7 +774,7 @@ private: { Env env{*this, features}; fund(env, gw, {alice, bob}, {USD(1'000)}, Fund::All); - AMM ammBob(env, bob, XRP(20'000), USD(200)); + AMM const ammBob(env, bob, XRP(20'000), USD(200)); // alice submits a tfSell | tfFillOrKill offer that crosses. // Even though tfSell is present it doesn't matter this time. env(offer(alice, USD(2), XRP(220), tfSell | tfFillOrKill)); @@ -791,7 +790,7 @@ private: // returns more than was asked for (because of the tfSell flag). Env env{*this, features}; fund(env, gw, {alice, bob}, {USD(1'000)}, Fund::All); - AMM ammBob(env, bob, XRP(20'000), USD(200)); + AMM const ammBob(env, bob, XRP(20'000), USD(200)); env(offer(alice, USD(10), XRP(1'500), tfSell | tfFillOrKill)); env.close(); @@ -810,7 +809,7 @@ private: // which matches alice's offer quality is ~ 10XRP/0.01996USD. Env env{*this, features}; fund(env, gw, {alice, bob}, {USD(10'000)}, Fund::All); - AMM ammBob(env, bob, XRP(5000), USD(10)); + AMM const ammBob(env, bob, XRP(5000), USD(10)); env(offer(alice, USD(1), XRP(501), tfSell | tfFillOrKill), ter(tecKILLED)); env.close(); @@ -877,7 +876,7 @@ private: // o carol has EUR but wants USD. // Note that Carol's offer must come last. If Carol's offer is // placed before AMM is created, then autobridging will not occur. - AMM ammAlice(env, alice, XRP(10'000), USD(10'100)); + AMM const ammAlice(env, alice, XRP(10'000), USD(10'100)); env(offer(bob, EUR(100), XRP(100))); env.close(); @@ -908,7 +907,7 @@ private: // o carol has EUR but wants USD. // Note that Carol's offer must come last. If Carol's offer is // placed before AMM is created, then autobridging will not occur. - AMM ammAlice(env, alice, XRP(10'000), USD(10'050)); + AMM const ammAlice(env, alice, XRP(10'000), USD(10'050)); env(offer(bob, EUR(100), XRP(100))); env.close(); @@ -954,7 +953,7 @@ private: // o carol has EUR but wants USD. // Note that Carol's offer must come last. If Carol's offer is // placed before AMM is created, then autobridging will not occur. - AMM ammAlice(env, alice, XRP(10'000), USD(10'100)); + AMM const ammAlice(env, alice, XRP(10'000), USD(10'100)); env(offer(bob, EUR(100), XRP(100))); env.close(); @@ -996,7 +995,7 @@ private: // o carol has EUR but wants USD. // Note that Carol's offer must come last. If Carol's offer is // placed before AMM is created, then autobridging will not occur. - AMM ammAlice(env, alice, XRP(10'000), USD(10'100)); + AMM const ammAlice(env, alice, XRP(10'000), USD(10'100)); env(offer(bob, EUR(100), XRP(100))); env.close(); @@ -1030,7 +1029,7 @@ private: env.fund(XRP(30'000) + f, alice, bob); env.close(); - AMM ammBob(env, bob, XRP(10'000), USD_bob(10'100)); + AMM const ammBob(env, bob, XRP(10'000), USD_bob(10'100)); env(offer(alice, USD_bob(100), XRP(100))); env.close(); @@ -1087,7 +1086,7 @@ private: BEAST_EXPECT(expectHolding(env, dan, A_BUX(none))); BEAST_EXPECT(expectHolding(env, dan, D_BUX(none))); - AMM ammBob(env, bob, A_BUX(30), D_BUX(30)); + AMM const ammBob(env, bob, A_BUX(30), D_BUX(30)); env(trust(ann, D_BUX(100))); env.close(); @@ -1144,7 +1143,7 @@ private: env(pay(bob, carol, B_BUX(400))); env(pay(ann, carol, A_BUX(400))); - AMM ammCarol(env, carol, A_BUX(300), B_BUX(330)); + AMM const ammCarol(env, carol, A_BUX(300), B_BUX(330)); // cam puts an offer on the books that her upcoming offer could cross. // But this offer should be deleted, not crossed, by her upcoming @@ -1216,7 +1215,7 @@ private: env(pay(gw, alice, USD(1'000))); env.close(); // Alice is able to create AMM since the GW has authorized her - AMM ammAlice(env, alice, USD(1'000), XRP(1'050)); + AMM const ammAlice(env, alice, USD(1'000), XRP(1'050)); // Set up authorized trust line for AMM. env(trust(gw, STAmount{Issue{USD.currency, ammAlice.ammAccount()}, 10}), @@ -1251,7 +1250,7 @@ private: // Alice doesn't have the funds { - AMM ammAlice(env, alice, USD(1'000), XRP(1'000), ter(tecUNFUNDED_AMM)); + AMM const ammAlice(env, alice, USD(1'000), XRP(1'000), ter(tecUNFUNDED_AMM)); } env(fset(gw, asfRequireAuth)); @@ -1268,7 +1267,7 @@ private: // Alice should not be able to create AMM without authorization. { - AMM ammAlice(env, alice, USD(1'000), XRP(1'000), ter(tecNO_LINE)); + AMM const ammAlice(env, alice, USD(1'000), XRP(1'000), ter(tecNO_LINE)); } // Set up a trust line for Alice, but don't authorize it. Alice @@ -1277,7 +1276,7 @@ private: env.close(); { - AMM ammAlice(env, alice, USD(1'000), XRP(1'000), ter(tecNO_AUTH)); + AMM const ammAlice(env, alice, USD(1'000), XRP(1'000), ter(tecNO_AUTH)); } // Finally, set up an authorized trust line for Alice. Now Alice's @@ -1287,7 +1286,7 @@ private: env(pay(gw, alice, USD(1'000))); env.close(); - AMM ammAlice(env, alice, USD(1'000), XRP(1'050)); + AMM const ammAlice(env, alice, USD(1'000), XRP(1'050)); // Set up authorized trust line for AMM. env(trust(gw, STAmount{Issue{USD.currency, ammAlice.ammAccount()}, 10}), @@ -1350,7 +1349,7 @@ private: env.fund(XRP(100'000'250), alice); fund(env, gw, {carol, bob}, {USD(100)}, Fund::All); fund(env, gw, {alice}, {USD(100)}, Fund::IOUOnly); - AMM ammCarol(env, carol, XRP(100), USD(100)); + AMM const ammCarol(env, carol, XRP(100), USD(100)); STPathSet st; STAmount sa; @@ -1384,7 +1383,7 @@ private: env.trust(AUD(2'000), bob, carol); env(pay(gw, carol, AUD(51))); env.close(); - AMM ammCarol(env, carol, XRP(40), AUD(51)); + AMM const ammCarol(env, carol, XRP(40), AUD(51)); env(pay(alice, bob, AUD(10)), sendmax(XRP(100)), paths(XRP)); env.close(); // AMM offer is 51.282052XRP/11AUD, 11AUD/1.1 = 10AUD to bob @@ -1405,7 +1404,7 @@ private: // XRP -> IOU receive max Env env = pathTestEnv(); fund(env, gw, {alice, bob, charlie}, {USD(11)}, Fund::All); - AMM ammCharlie(env, charlie, XRP(10), USD(11)); + AMM const ammCharlie(env, charlie, XRP(10), USD(11)); auto [st, sa, da] = find_paths(env, alice, bob, USD(-1), XRP(1).value()); BEAST_EXPECT(sa == XRP(1)); BEAST_EXPECT(equal(da, USD(1))); @@ -1421,7 +1420,7 @@ private: // IOU -> XRP receive max Env env = pathTestEnv(); fund(env, gw, {alice, bob, charlie}, {USD(11)}, Fund::All); - AMM ammCharlie(env, charlie, XRP(11), USD(10)); + AMM const ammCharlie(env, charlie, XRP(11), USD(10)); env.close(); auto [st, sa, da] = find_paths(env, alice, bob, drops(-1), USD(1).value()); BEAST_EXPECT(sa == USD(1)); @@ -1442,13 +1441,13 @@ private: testcase("Path Find: XRP -> XRP and XRP -> IOU"); using namespace jtx; Env env = pathTestEnv(); - Account A1{"A1"}; - Account A2{"A2"}; - Account A3{"A3"}; - Account G1{"G1"}; - Account G2{"G2"}; - Account G3{"G3"}; - Account M1{"M1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const A3{"A3"}; + Account const G1{"G1"}; + Account const G2{"G2"}; + Account const G3{"G3"}; + Account const M1{"M1"}; env.fund(XRP(100'000), A1); env.fund(XRP(10'000), A2); @@ -1473,8 +1472,8 @@ private: env(pay(G3, M1, G3["ABC"](25'000))); env.close(); - AMM ammM1_G1_G2(env, M1, G1["XYZ"](1'000), G2["XYZ"](1'000)); - AMM ammM1_XRP_G3(env, M1, XRP(10'000), G3["ABC"](1'000)); + AMM const ammM1_G1_G2(env, M1, G1["XYZ"](1'000), G2["XYZ"](1'000)); + AMM const ammM1_XRP_G3(env, M1, XRP(10'000), G3["ABC"](1'000)); STPathSet st; STAmount sa, da; @@ -1527,10 +1526,10 @@ private: testcase("Path Find: non-XRP -> XRP"); using namespace jtx; Env env = pathTestEnv(); - Account A1{"A1"}; - Account A2{"A2"}; - Account G3{"G3"}; - Account M1{"M1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const G3{"G3"}; + Account const M1{"M1"}; env.fund(XRP(1'000), A1, A2, G3); env.fund(XRP(11'000), M1); @@ -1545,7 +1544,7 @@ private: env(pay(G3, M1, G3["ABC"](1'200))); env.close(); - AMM ammM1(env, M1, G3["ABC"](1'000), XRP(10'010)); + AMM const ammM1(env, M1, G3["ABC"](1'000), XRP(10'010)); STPathSet st; STAmount sa, da; @@ -1563,16 +1562,16 @@ private: testcase("Path Find: non-XRP -> non-XRP, same currency"); using namespace jtx; Env env = pathTestEnv(); - Account A1{"A1"}; - Account A2{"A2"}; - Account A3{"A3"}; - Account A4{"A4"}; - Account G1{"G1"}; - Account G2{"G2"}; - Account G3{"G3"}; - Account G4{"G4"}; - Account M1{"M1"}; - Account M2{"M2"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const A3{"A3"}; + Account const A4{"A4"}; + Account const G1{"G1"}; + Account const G2{"G2"}; + Account const G3{"G3"}; + Account const G4{"G4"}; + Account const M1{"M1"}; + Account const M2{"M2"}; env.fund(XRP(1'000), A1, A2, A3, G1, G2, G3, G4); env.fund(XRP(10'000), A4); @@ -1597,9 +1596,9 @@ private: env(pay(G2, M2, G2["HKD"](5'000))); env.close(); - AMM ammM1(env, M1, G1["HKD"](1'010), G2["HKD"](1'000)); - AMM ammM2XRP_G2(env, M2, XRP(10'000), G2["HKD"](1'010)); - AMM ammM2G1_XRP(env, M2, G1["HKD"](1'010), XRP(10'000)); + AMM const ammM1(env, M1, G1["HKD"](1'010), G2["HKD"](1'000)); + AMM const ammM2XRP_G2(env, M2, XRP(10'000), G2["HKD"](1'010)); + AMM const ammM2G1_XRP(env, M2, G1["HKD"](1'010), XRP(10'000)); STPathSet st; STAmount sa, da; @@ -1693,12 +1692,12 @@ private: testcase("Path Find: non-XRP -> non-XRP, same currency"); using namespace jtx; Env env = pathTestEnv(); - Account A1{"A1"}; - Account A2{"A2"}; - Account A3{"A3"}; - Account G1{"G1"}; - Account G2{"G2"}; - Account M1{"M1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const A3{"A3"}; + Account const G1{"G1"}; + Account const G2{"G2"}; + Account const M1{"M1"}; env.fund(XRP(11'000), M1); env.fund(XRP(1'000), A1, A2, A3, G1, G2); @@ -1717,7 +1716,7 @@ private: env(pay(G2, M1, G2["HKD"](5'000))); env.close(); - AMM ammM1(env, M1, G1["HKD"](1'010), G2["HKD"](1'000)); + AMM const ammM1(env, M1, G1["HKD"](1'010), G2["HKD"](1'000)); // E) Gateway to user // Source -> OB -> AC -> Destination @@ -1760,7 +1759,7 @@ private: // tecPATH_DRY, but the entire path should not be marked as dry. // This is the second error case to test (when flowV1 is used). env(offer(bob, EUR(50), XRP(50))); - AMM ammBob(env, bob, ammXrpPool, USD(150)); + AMM const ammBob(env, bob, ammXrpPool, USD(150)); env(pay(alice, carol, USD(1'000'000)), path(~XRP, ~USD), @@ -1784,7 +1783,7 @@ private: fund(env, gw, {alice, bob, carol}, XRP(10'000), {BTC(100), USD(150)}, Fund::All); - AMM ammBob(env, bob, BTC(100), USD(150)); + AMM const ammBob(env, bob, BTC(100), USD(150)); env(pay(alice, carol, USD(50)), path(~USD), sendmax(BTC(50))); @@ -1800,8 +1799,8 @@ private: fund(env, gw, {alice, carol, bob}, XRP(10'000), {BTC(100), USD(150)}, Fund::All); - AMM ammBobBTC_XRP(env, bob, BTC(100), XRP(150)); - AMM ammBobXRP_USD(env, bob, XRP(100), USD(150)); + AMM const ammBobBTC_XRP(env, bob, BTC(100), XRP(150)); + AMM const ammBobXRP_USD(env, bob, XRP(100), USD(150)); env(pay(alice, carol, USD(50)), path(~XRP, ~USD), sendmax(BTC(50))); @@ -1818,7 +1817,7 @@ private: fund(env, gw, {alice, carol, bob}, XRP(10'000), {USD(150)}, Fund::All); - AMM ammBob(env, bob, XRP(100), USD(150)); + AMM const ammBob(env, bob, XRP(100), USD(150)); env(pay(alice, carol, USD(50)), path(~USD), sendmax(XRP(50))); @@ -1834,7 +1833,7 @@ private: fund(env, gw, {alice, carol, bob}, XRP(10'000), {USD(100)}, Fund::All); - AMM ammBob(env, bob, USD(100), XRP(150)); + AMM const ammBob(env, bob, USD(100), XRP(150)); env(pay(alice, carol, XRP(50)), path(~XRP), sendmax(USD(50))); @@ -1864,7 +1863,7 @@ private: env(offer(bob, BTC(50), USD(50))); env(offer(bob, BTC(40), EUR(50))); env.close(); - AMM ammBob(env, bob, EUR(100), USD(150)); + AMM const ammBob(env, bob, EUR(100), USD(150)); // unfund offer env(pay(bob, gw, EUR(50))); @@ -1915,7 +1914,7 @@ private: env.close(); // This is multiplath, which generates limited # of offers - AMM ammBobBTC_USD(env, bob, BTC(50), USD(50)); + AMM const ammBobBTC_USD(env, bob, BTC(50), USD(50)); env(offer(bob, BTC(60), EUR(50))); env(offer(carol, BTC(1'000), EUR(1))); env(offer(bob, EUR(50), USD(50))); @@ -1927,9 +1926,9 @@ private: BEAST_EXPECT(isOffer(env, carol, BTC(1'000), EUR(1))); BEAST_EXPECT(isOffer(env, bob, EUR(50), USD(50))); - auto flowJournal = env.app().logs().journal("Flow"); + auto flowJournal = env.app().getJournal("Flow"); auto const flowResult = [&] { - STAmount deliver(USD(51)); + STAmount const deliver(USD(51)); STAmount smax(BTC(61)); PaymentSandbox sb(env.current().get(), tapNONE); STPathSet paths; @@ -1942,10 +1941,10 @@ private: }; { // BTC -> USD - STPath p1({IPE(USD.issue())}); + STPath const p1({IPE(USD.issue())}); paths.push_back(p1); // BTC -> EUR -> USD - STPath p2({IPE(EUR.issue()), IPE(USD.issue())}); + STPath const p2({IPE(EUR.issue()), IPE(USD.issue())}); paths.push_back(p2); } @@ -1966,13 +1965,15 @@ private: }(); BEAST_EXPECT(flowResult.removableOffers.size() == 1); - env.app().openLedger().modify([&](OpenView& view, beast::Journal j) { + env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) { if (flowResult.removableOffers.empty()) return false; Sandbox sb(&view, tapNONE); for (auto const& o : flowResult.removableOffers) + { if (auto ok = sb.peek(keylet::offer(o))) offerDelete(sb, ok, flowJournal); + } sb.apply(view); return true; }); @@ -2006,7 +2007,7 @@ private: env.close(); // env(offer(bob, USD(1), drops(2)), txflags(tfPassive)); - AMM ammBob(env, bob, USD(8), XRPAmount{21}); + AMM const ammBob(env, bob, USD(8), XRPAmount{21}); env(offer(bob, drops(1), EUR(1'000)), txflags(tfPassive)); env(pay(alice, carol, EUR(1)), @@ -2033,7 +2034,7 @@ private: env(rate(gw, 1.25)); env.close(); - AMM amm(env, bob, GBP(1'000), USD(1'000)); + AMM const amm(env, bob, GBP(1'000), USD(1'000)); env(pay(alice, carol, USD(100)), path(~USD), @@ -2074,7 +2075,7 @@ private: env(offer(ed, GBP(1'000), EUR(1'000)), txflags(tfPassive)); env.close(); - AMM amm(env, bob, EUR(1'000), USD(1'000)); + AMM const amm(env, bob, EUR(1'000), USD(1'000)); env(pay(alice, carol, USD(100)), path(~EUR, ~USD), @@ -2108,8 +2109,8 @@ private: env(rate(gw, 1.25)); env.close(); - AMM amm1(env, bob, GBP(1'000), EUR(1'000)); - AMM amm2(env, ed, EUR(1'000), USD(1'000)); + AMM const amm1(env, bob, GBP(1'000), EUR(1'000)); + AMM const amm2(env, ed, EUR(1'000), USD(1'000)); env(pay(alice, carol, USD(100)), path(~EUR, ~USD), @@ -2158,7 +2159,7 @@ private: env(rate(gw, 1.25)); env.close(); - AMM amm(env, bob, USD(1'000), EUR(1'100)); + AMM const amm(env, bob, USD(1'000), EUR(1'100)); env(offer(alice, EUR(100), USD(100))); env.close(); @@ -2177,7 +2178,7 @@ private: env(rate(gw, 1.25)); env.close(); - AMM amm(env, bob, GBP(1'000), USD(1'000)); + AMM const amm(env, bob, GBP(1'000), USD(1'000)); // requested quality limit is 100USD/178.58GBP = 0.55997 // trade quality is 100USD/178.5714 = 0.55999 @@ -2208,7 +2209,7 @@ private: env(rate(gw, 1.25)); env.close(); - AMM amm(env, bob, GBP(1'000), USD(1'200)); + AMM const amm(env, bob, GBP(1'000), USD(1'200)); // requested quality limit is 90USD/120GBP = 0.75 // trade quality is 22.5USD/30GBP = 0.75 @@ -2258,7 +2259,7 @@ private: env(offer(ed, GBP(1'000), EUR(1'000)), txflags(tfPassive)); env.close(); - AMM amm(env, bob, EUR(1'000), USD(1'400)); + AMM const amm(env, bob, EUR(1'000), USD(1'400)); // requested quality limit is 95USD/140GBP = 0.6785 // trade quality is 59.7321USD/88.0262GBP = 0.6785 @@ -2339,7 +2340,7 @@ private: env(rate(gw, 1.25)); env.close(); - AMM amm(env, bob, GBP(1'000), EUR(1'000)); + AMM const amm(env, bob, GBP(1'000), EUR(1'000)); env(offer(ed, EUR(1'000), USD(1'400)), txflags(tfPassive)); env.close(); @@ -2411,8 +2412,8 @@ private: env(rate(gw, 1.25)); env.close(); - AMM amm1(env, bob, GBP(1'000), EUR(1'000)); - AMM amm2(env, ed, EUR(1'000), USD(1'400)); + AMM const amm1(env, bob, GBP(1'000), EUR(1'000)); + AMM const amm2(env, ed, EUR(1'000), USD(1'400)); // requested quality limit is 90USD/145GBP = 0.6206 // trade quality is 66.7432USD/107.5308GBP = 0.6206 @@ -2473,8 +2474,8 @@ private: env(rate(gw, 1.25)); env.close(); - AMM amm1(env, alice, GBP(1'000), EUR(1'000)); - AMM amm2(env, bob, EUR(1'000), USD(1'400)); + AMM const amm1(env, alice, GBP(1'000), EUR(1'000)); + AMM const amm2(env, bob, EUR(1'000), USD(1'400)); // requested quality limit is 90USD/120GBP = 0.75 // trade quality is 81.1111USD/108.1481GBP = 0.75 @@ -2533,7 +2534,7 @@ private: fund(env, gw, {alice, bob, carol}, XRP(10'000), {USD(2'000)}); - AMM ammBob(env, bob, XRP(1'000), USD(1'050)); + AMM const ammBob(env, bob, XRP(1'000), USD(1'050)); env(offer(bob, XRP(100), USD(50))); env(pay(alice, carol, USD(100)), @@ -2560,8 +2561,8 @@ private: // fails with tecPATH_DRY. fund(env, gw, {alice, bob}, XRP(10'000), {USD(200), EUR(200)}, Fund::All); - AMM ammAliceXRP_USD(env, alice, XRP(100), USD(101)); - AMM ammAliceXRP_EUR(env, alice, XRP(100), EUR(101)); + AMM const ammAliceXRP_USD(env, alice, XRP(100), USD(101)); + AMM const ammAliceXRP_EUR(env, alice, XRP(100), EUR(101)); env.close(); TER const expectedTer = TER{temBAD_PATH_LOOP}; @@ -2578,8 +2579,8 @@ private: // with tecPATH_DRY. fund(env, gw, {alice, bob}, XRP(10'000), {USD(200), EUR(200)}, Fund::All); - AMM ammAliceXRP_USD(env, alice, XRP(100), USD(100)); - AMM ammAliceXRP_EUR(env, alice, XRP(100), EUR(100)); + AMM const ammAliceXRP_USD(env, alice, XRP(100), USD(100)); + AMM const ammAliceXRP_EUR(env, alice, XRP(100), EUR(100)); // EUR -> //XRP -> //USD ->XRP env(pay(alice, bob, XRP(1)), path(~XRP, ~USD, ~XRP), @@ -2596,9 +2597,9 @@ private: // with tecPATH_DRY. fund(env, gw, {alice, bob}, XRP(10'000), {USD(200), EUR(200), JPY(200)}, Fund::All); - AMM ammAliceXRP_USD(env, alice, XRP(100), USD(100)); - AMM ammAliceXRP_EUR(env, alice, XRP(100), EUR(100)); - AMM ammAliceXRP_JPY(env, alice, XRP(100), JPY(100)); + AMM const ammAliceXRP_USD(env, alice, XRP(100), USD(100)); + AMM const ammAliceXRP_EUR(env, alice, XRP(100), EUR(100)); + AMM const ammAliceXRP_JPY(env, alice, XRP(100), JPY(100)); env(pay(alice, bob, JPY(1)), path(~XRP, ~EUR, ~XRP, ~JPY), @@ -2627,15 +2628,19 @@ private: env(pay(gw, dan, USD(1))); n_offers(env, 2'000, bob, XRP(1), USD(1)); n_offers(env, 1, dan, XRP(1), USD(1)); - AMM ammEd(env, ed, XRP(9), USD(11)); + AMM const ammEd(env, ed, XRP(9), USD(11)); // Alice offers to buy 1000 XRP for 1000 USD. She takes Bob's first // offer, removes 999 more as unfunded, then hits the step limit. env(offer(alice, USD(1'000), XRP(1'000))); if (!features[fixAMMv1_1]) + { env.require(balance(alice, STAmount{USD, UINT64_C(2'050126257867561), -15})); + } else + { env.require(balance(alice, STAmount{USD, UINT64_C(2'050125257867587), -15})); + } env.require(owners(alice, 2)); env.require(balance(bob, USD(0))); env.require(owners(bob, 1'001)); @@ -2682,7 +2687,7 @@ private: txflags(tfPartialPayment), ter(temBAD_AMOUNT)); env(pay(gw, carol, USD(50))); - AMM ammCarol(env, carol, XRP(10), USD(15)); + AMM const ammCarol(env, carol, XRP(10), USD(15)); env(pay(alice, bob, USD(10)), paths(XRP), deliver_min(USD(7)), @@ -2698,7 +2703,7 @@ private: fund(env, gw, {alice, bob}, XRP(10'000)); env.trust(USD(1'100), alice, bob); env(pay(gw, bob, USD(1'100))); - AMM ammBob(env, bob, XRP(1'000), USD(1'100)); + AMM const ammBob(env, bob, XRP(1'000), USD(1'100)); env(pay(alice, alice, USD(10'000)), paths(XRP), deliver_min(USD(100)), @@ -2712,7 +2717,7 @@ private: fund(env, gw, {alice, bob, carol}, XRP(10'000)); env.trust(USD(1'200), bob, carol); env(pay(gw, bob, USD(1'200))); - AMM ammBob(env, bob, XRP(5'500), USD(1'200)); + AMM const ammBob(env, bob, XRP(5'500), USD(1'200)); env(pay(alice, carol, USD(10'000)), paths(XRP), deliver_min(USD(200)), @@ -2738,7 +2743,7 @@ private: env(pay(gw, dan, USD(1'100))); env(offer(bob, XRP(100), USD(100))); env(offer(bob, XRP(1'000), USD(100))); - AMM ammDan(env, dan, XRP(1'000), USD(1'100)); + AMM const ammDan(env, dan, XRP(1'000), USD(1'100)); if (!features[fixAMMv1_1]) { env(pay(alice, carol, USD(10'000)), @@ -2789,7 +2794,7 @@ private: env(pay(gw, alice, USD(500))); env.close(); - AMM ammAlice(env, alice, XRP(100), USD(140)); + AMM const ammAlice(env, alice, XRP(100), USD(140)); // becky pays herself USD (10) by consuming part of alice's offer. // Make sure the payment works if PaymentAuth is not involved. @@ -2824,7 +2829,7 @@ private: env(pay(gw, alice, USD(150))); env(pay(gw, carol, USD(150))); - AMM ammCarol(env, carol, USD(100), XRPAmount(101)); + AMM const ammCarol(env, carol, USD(100), XRPAmount(101)); // Make sure bob's trust line is all set up so he can receive USD. env(pay(alice, bob, USD(50))); @@ -2921,7 +2926,7 @@ private: env(pay(G1, alice, G1["USD"](205))); env.close(); - AMM ammAlice(env, alice, XRP(500), G1["USD"](105)); + AMM const ammAlice(env, alice, XRP(500), G1["USD"](105)); { auto lines = getAccountLines(env, bob); @@ -3035,11 +3040,11 @@ private: using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; - Account A2{"A2"}; - Account A3{"A3"}; - Account A4{"A4"}; + Account const G1{"G1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const A3{"A3"}; + Account const A4{"A4"}; env.fund(XRP(12'000), G1); env.fund(XRP(1'000), A1); @@ -3058,7 +3063,7 @@ private: env(pay(G1, A4, G1["BTC"](100))); env.close(); - AMM ammG1(env, G1, XRP(10'000), G1["USD"](100)); + AMM const ammG1(env, G1, XRP(10'000), G1["USD"](100)); env(offer(A1, XRP(10'000), G1["USD"](100)), txflags(tfPassive)); env(offer(A2, G1["USD"](100), XRP(10'000)), txflags(tfPassive)); env.close(); @@ -3124,7 +3129,7 @@ private: env.require(nflags(G1, asfNoFreeze)); // test: assets can't be bought on the market - AMM ammA3(env, A3, G1["BTC"](1), XRP(1), ter(tecFROZEN)); + AMM const ammA3(env, A3, G1["BTC"](1), XRP(1), ter(tecFROZEN)); // test: assets can't be sold on the market // AMM is bidirectional @@ -3165,10 +3170,10 @@ private: using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A2{"A2"}; - Account A3{"A3"}; - Account A4{"A4"}; + Account const G1{"G1"}; + Account const A2{"A2"}; + Account const A3{"A3"}; + Account const A4{"A4"}; env.fund(XRP(2'000), G1, A3, A4); env.fund(XRP(2'000), A2); @@ -3183,7 +3188,7 @@ private: env(pay(G1, A4, G1["USD"](2'001))); env.close(); - AMM ammA3(env, A3, XRP(1'000), G1["USD"](1'001)); + AMM const ammA3(env, A3, XRP(1'000), G1["USD"](1'001)); // removal after successful payment // test: make a payment with partially consuming offer @@ -3294,8 +3299,8 @@ private: fund(env, gw, {alice, bob, carol}, XRP(10'000), {USD(2'000), EUR(1'000)}); - AMM bobXRP_USD(env, bob, XRP(1'000), USD(1'000)); - AMM bobUSD_EUR(env, bob, USD(1'000), EUR(1'000)); + AMM const bobXRP_USD(env, bob, XRP(1'000), USD(1'000)); + AMM const bobUSD_EUR(env, bob, USD(1'000), EUR(1'000)); // payment path: XRP -> XRP/USD -> USD/EUR -> EUR/USD env(pay(alice, carol, USD(100)), @@ -3322,10 +3327,10 @@ private: fund(env, bob, {alice, gw}, {BobUSD(100), BobEUR(100)}, Fund::IOUOnly); env.close(); - AMM ammBobXRP_USD(env, bob, XRP(100), BobUSD(100)); + AMM const ammBobXRP_USD(env, bob, XRP(100), BobUSD(100)); env(offer(gw, XRP(100), USD(100)), txflags(tfPassive)); - AMM ammBobUSD_EUR(env, bob, BobUSD(100), BobEUR(100)); + AMM const ammBobUSD_EUR(env, bob, BobUSD(100), BobEUR(100)); env(offer(gw, USD(100), EUR(100)), txflags(tfPassive)); Path const p = [&] { @@ -3335,7 +3340,7 @@ private: return result; }(); - PathSet paths(p); + PathSet const paths(p); env(pay(alice, alice, EUR(1)), json(paths.json()), @@ -3349,7 +3354,7 @@ private: fund(env, gw, {alice, bob, carol}, XRP(10'000), {USD(100)}); - AMM ammBob(env, bob, XRP(100), USD(100)); + AMM const ammBob(env, bob, XRP(100), USD(100)); // payment path: XRP -> XRP/USD -> USD/XRP env(pay(alice, carol, XRP(100)), @@ -3363,7 +3368,7 @@ private: fund(env, gw, {alice, bob, carol}, XRP(10'000), {USD(100)}); - AMM ammBob(env, bob, XRP(100), USD(100)); + AMM const ammBob(env, bob, XRP(100), USD(100)); // payment path: XRP -> XRP/USD -> USD/XRP env(pay(alice, carol, XRP(100)), @@ -3393,7 +3398,7 @@ private: env(pay(gw, alice, USD(100))); env.close(); - AMM ammBob(env, bob, XRP(100), USD(100)); + AMM const ammBob(env, bob, XRP(100), USD(100)); // payment path: USD -> USD/XRP -> XRP/USD env(pay(alice, carol, USD(100)), @@ -3416,9 +3421,9 @@ private: env(pay(gw, bob, EUR(200))); env(pay(gw, bob, CNY(100))); - AMM ammBobXRP_USD(env, bob, XRP(100), USD(100)); - AMM ammBobUSD_EUR(env, bob, USD(100), EUR(100)); - AMM ammBobEUR_CNY(env, bob, EUR(100), CNY(100)); + AMM const ammBobXRP_USD(env, bob, XRP(100), USD(100)); + AMM const ammBobUSD_EUR(env, bob, USD(100), EUR(100)); + AMM const ammBobEUR_CNY(env, bob, EUR(100), CNY(100)); // payment path: XRP->XRP/USD->USD/EUR->USD/CNY env(pay(alice, carol, CNY(100)), diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index 71dd5cc1c0..75b29fb79d 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -7,13 +7,14 @@ #include #include +#include +#include +#include #include #include #include #include #include -#include -#include #include @@ -86,7 +87,7 @@ private: env(rate(gw, 1.25)); env.close(); // no transfer fee on create - AMM ammAlice(env, alice, USD(20'000), BTC(0.5)); + AMM const ammAlice(env, alice, USD(20'000), BTC(0.5)); BEAST_EXPECT(ammAlice.expectBalances(USD(20'000), BTC(0.5), IOUAmount{100, 0})); BEAST_EXPECT(expectHolding(env, alice, USD(0))); BEAST_EXPECT(expectHolding(env, alice, BTC(0))); @@ -103,7 +104,7 @@ private: env.close(); env(pay(gw, alice, USD(10'000))); env.close(); - AMM ammAlice(env, alice, XRP(10'000), USD(10'000)); + AMM const ammAlice(env, alice, XRP(10'000), USD(10'000)); } // Cleared global freeze @@ -117,10 +118,10 @@ private: env.close(); env(fset(gw, asfGlobalFreeze)); env.close(); - AMM ammAliceFail(env, alice, XRP(10'000), USD(10'000), ter(tecFROZEN)); + AMM const ammAliceFail(env, alice, XRP(10'000), USD(10'000), ter(tecFROZEN)); env(fclear(gw, asfGlobalFreeze)); env.close(); - AMM ammAlice(env, alice, XRP(10'000), USD(10'000)); + AMM const ammAlice(env, alice, XRP(10'000), USD(10'000)); } // Trading fee @@ -152,7 +153,7 @@ private: { Env env{*this}; fund(env, gw, {alice}, {USD(30'000)}, Fund::All); - AMM ammAlice(env, alice, XRP(10'000), XRP(10'000), ter(temBAD_AMM_TOKENS)); + AMM const ammAlice(env, alice, XRP(10'000), XRP(10'000), ter(temBAD_AMM_TOKENS)); BEAST_EXPECT(!ammAlice.ammExists()); } @@ -160,7 +161,7 @@ private: { Env env{*this}; fund(env, gw, {alice}, {USD(30'000)}, Fund::All); - AMM ammAlice(env, alice, USD(10'000), USD(10'000), ter(temBAD_AMM_TOKENS)); + AMM const ammAlice(env, alice, USD(10'000), USD(10'000), ter(temBAD_AMM_TOKENS)); BEAST_EXPECT(!ammAlice.ammExists()); } @@ -168,13 +169,13 @@ private: { Env env{*this}; fund(env, gw, {alice}, {USD(30'000)}, Fund::All); - AMM ammAlice(env, alice, XRP(0), USD(10'000), ter(temBAD_AMOUNT)); + AMM const ammAlice(env, alice, XRP(0), USD(10'000), ter(temBAD_AMOUNT)); BEAST_EXPECT(!ammAlice.ammExists()); - AMM ammAlice1(env, alice, XRP(10'000), USD(0), ter(temBAD_AMOUNT)); + AMM const ammAlice1(env, alice, XRP(10'000), USD(0), ter(temBAD_AMOUNT)); BEAST_EXPECT(!ammAlice1.ammExists()); - AMM ammAlice2(env, alice, XRP(10'000), USD(-10'000), ter(temBAD_AMOUNT)); + AMM const ammAlice2(env, alice, XRP(10'000), USD(-10'000), ter(temBAD_AMOUNT)); BEAST_EXPECT(!ammAlice2.ammExists()); - AMM ammAlice3(env, alice, XRP(-10'000), USD(10'000), ter(temBAD_AMOUNT)); + AMM const ammAlice3(env, alice, XRP(-10'000), USD(10'000), ter(temBAD_AMOUNT)); BEAST_EXPECT(!ammAlice3.ammExists()); } @@ -182,7 +183,7 @@ private: { Env env{*this}; fund(env, gw, {alice}, {USD(30'000)}, Fund::All); - AMM ammAlice(env, alice, XRP(10'000), BAD(10'000), ter(temBAD_CURRENCY)); + AMM const ammAlice(env, alice, XRP(10'000), BAD(10'000), ter(temBAD_CURRENCY)); BEAST_EXPECT(!ammAlice.ammExists()); } @@ -190,7 +191,7 @@ private: { Env env{*this}; fund(env, gw, {alice}, {USD(30'000)}, Fund::All); - AMM ammAlice(env, alice, XRP(10'000), USD(40'000), ter(tecUNFUNDED_AMM)); + AMM const ammAlice(env, alice, XRP(10'000), USD(40'000), ter(tecUNFUNDED_AMM)); BEAST_EXPECT(!ammAlice.ammExists()); } @@ -198,7 +199,7 @@ private: { Env env{*this}; fund(env, gw, {alice}, {USD(30'000)}, Fund::All); - AMM ammAlice(env, alice, XRP(40'000), USD(10'000), ter(tecUNFUNDED_AMM)); + AMM const ammAlice(env, alice, XRP(40'000), USD(10'000), ter(tecUNFUNDED_AMM)); BEAST_EXPECT(!ammAlice.ammExists()); } @@ -206,7 +207,7 @@ private: { Env env{*this}; fund(env, gw, {alice}, {USD(30'000)}, Fund::All); - AMM ammAlice( + AMM const ammAlice( env, alice, XRP(10'000), @@ -223,14 +224,14 @@ private: // AMM already exists testAMM([&](AMM& ammAlice, Env& env) { - AMM ammCarol(env, carol, XRP(10'000), USD(10'000), ter(tecDUPLICATE)); + AMM const ammCarol(env, carol, XRP(10'000), USD(10'000), ter(tecDUPLICATE)); }); // Invalid flags { Env env{*this}; fund(env, gw, {alice}, {USD(30'000)}, Fund::All); - AMM ammAlice( + AMM const ammAlice( env, alice, XRP(10'000), @@ -248,9 +249,9 @@ private: // Invalid Account { Env env{*this}; - Account bad("bad"); + Account const bad("bad"); env.memoize(bad); - AMM ammAlice( + AMM const ammAlice( env, bad, XRP(10'000), @@ -274,7 +275,7 @@ private: env.close(); env(trust(gw, alice["USD"](30'000))); env.close(); - AMM ammAlice(env, alice, XRP(10'000), USD(10'000), ter(tecNO_AUTH)); + AMM const ammAlice(env, alice, XRP(10'000), USD(10'000), ter(tecNO_AUTH)); BEAST_EXPECT(!ammAlice.ammExists()); } @@ -287,7 +288,7 @@ private: env.close(); env(trust(gw, alice["USD"](30'000))); env.close(); - AMM ammAlice(env, alice, XRP(10'000), USD(10'000), ter(tecFROZEN)); + AMM const ammAlice(env, alice, XRP(10'000), USD(10'000), ter(tecFROZEN)); BEAST_EXPECT(!ammAlice.ammExists()); } @@ -300,7 +301,7 @@ private: env.close(); env(trust(gw, alice["USD"](0), tfSetFreeze)); env.close(); - AMM ammAlice(env, alice, XRP(10'000), USD(10'000), ter(tecFROZEN)); + AMM const ammAlice(env, alice, XRP(10'000), USD(10'000), ter(tecFROZEN)); BEAST_EXPECT(!ammAlice.ammExists()); } @@ -316,7 +317,7 @@ private: env.close(); env(offer(alice, XRP(101), USD(100))); env(offer(alice, XRP(102), USD(100))); - AMM ammAlice(env, alice, XRP(1'000), USD(1'000), ter(tecUNFUNDED_AMM)); + AMM const ammAlice(env, alice, XRP(1'000), USD(1'000), ter(tecUNFUNDED_AMM)); } // Insufficient reserve, IOU/IOU @@ -333,14 +334,14 @@ private: env.close(); env(offer(alice, EUR(101), USD(100))); env(offer(alice, EUR(102), USD(100))); - AMM ammAlice(env, alice, EUR(1'000), USD(1'000), ter(tecINSUF_RESERVE_LINE)); + AMM const ammAlice(env, alice, EUR(1'000), USD(1'000), ter(tecINSUF_RESERVE_LINE)); } // Insufficient fee { Env env(*this); fund(env, gw, {alice}, XRP(2'000), {USD(2'000), EUR(2'000)}); - AMM ammAlice( + AMM const ammAlice( env, alice, EUR(1'000), @@ -359,13 +360,13 @@ private: // AMM with one LPToken from another AMM. testAMM([&](AMM& ammAlice, Env& env) { fund(env, gw, {alice}, {EUR(10'000)}, Fund::IOUOnly); - AMM ammAMMToken( + AMM const ammAMMToken( env, alice, EUR(10'000), STAmount{ammAlice.lptIssue(), 1'000'000}, ter(tecAMM_INVALID_TOKENS)); - AMM ammAMMToken1( + AMM const ammAMMToken1( env, alice, STAmount{ammAlice.lptIssue(), 1'000'000}, @@ -376,10 +377,10 @@ private: // AMM with two LPTokens from other AMMs. testAMM([&](AMM& ammAlice, Env& env) { fund(env, gw, {alice}, {EUR(10'000)}, Fund::IOUOnly); - AMM ammAlice1(env, alice, XRP(10'000), EUR(10'000)); + AMM const ammAlice1(env, alice, XRP(10'000), EUR(10'000)); auto const token1 = ammAlice.lptIssue(); auto const token2 = ammAlice1.lptIssue(); - AMM ammAMMTokens( + AMM const ammAMMTokens( env, alice, STAmount{token1, 1'000'000}, @@ -392,21 +393,21 @@ private: Env env(*this); env.fund(XRP(30'000), gw); env(fclear(gw, asfDefaultRipple)); - AMM ammGw(env, gw, XRP(10'000), USD(10'000), ter(terNO_RIPPLE)); + AMM const ammGw(env, gw, XRP(10'000), USD(10'000), ter(terNO_RIPPLE)); env.fund(XRP(30'000), alice); env.trust(USD(30'000), alice); env(pay(gw, alice, USD(30'000))); - AMM ammAlice(env, alice, XRP(10'000), USD(10'000), ter(terNO_RIPPLE)); + AMM const ammAlice(env, alice, XRP(10'000), USD(10'000), ter(terNO_RIPPLE)); Account const gw1("gw1"); env.fund(XRP(30'000), gw1); env(fclear(gw1, asfDefaultRipple)); env.trust(USD(30'000), gw1); env(pay(gw, gw1, USD(30'000))); auto const USD1 = gw1["USD"]; - AMM ammGwGw1(env, gw, USD(10'000), USD1(10'000), ter(terNO_RIPPLE)); + AMM const ammGwGw1(env, gw, USD(10'000), USD1(10'000), ter(terNO_RIPPLE)); env.trust(USD1(30'000), alice); env(pay(gw1, alice, USD1(30'000))); - AMM ammAlice1(env, alice, USD(10'000), USD1(10'000), ter(terNO_RIPPLE)); + AMM const ammAlice1(env, alice, USD(10'000), USD1(10'000), ter(terNO_RIPPLE)); } } @@ -428,106 +429,65 @@ private: std::optional, std::optional, std::optional, - std::optional>> - invalidOptions = { - // flags, tokens, asset1In, asset2in, EPrice, tfee - {tfLPToken, 1'000, std::nullopt, USD(100), std::nullopt, std::nullopt}, - {tfLPToken, 1'000, XRP(100), std::nullopt, std::nullopt, std::nullopt}, - {tfLPToken, - 1'000, - std::nullopt, - std::nullopt, - STAmount{USD, 1, -1}, - std::nullopt}, - {tfLPToken, - std::nullopt, - USD(100), - std::nullopt, - STAmount{USD, 1, -1}, - std::nullopt}, - {tfLPToken, 1'000, XRP(100), std::nullopt, STAmount{USD, 1, -1}, std::nullopt}, - {tfLPToken, 1'000, std::nullopt, std::nullopt, std::nullopt, 1'000}, - {tfSingleAsset, 1'000, std::nullopt, std::nullopt, std::nullopt, std::nullopt}, - {tfSingleAsset, - std::nullopt, - std::nullopt, - USD(100), - std::nullopt, - std::nullopt}, - {tfSingleAsset, - std::nullopt, - std::nullopt, - std::nullopt, - STAmount{USD, 1, -1}, - std::nullopt}, - {tfSingleAsset, std::nullopt, USD(100), std::nullopt, std::nullopt, 1'000}, - {tfTwoAsset, 1'000, std::nullopt, std::nullopt, std::nullopt, std::nullopt}, - {tfTwoAsset, - std::nullopt, - XRP(100), - USD(100), - STAmount{USD, 1, -1}, - std::nullopt}, - {tfTwoAsset, std::nullopt, XRP(100), std::nullopt, std::nullopt, std::nullopt}, - {tfTwoAsset, std::nullopt, XRP(100), USD(100), std::nullopt, 1'000}, - {tfTwoAsset, - std::nullopt, - std::nullopt, - USD(100), - STAmount{USD, 1, -1}, - std::nullopt}, - {tfOneAssetLPToken, - 1'000, - std::nullopt, - std::nullopt, - std::nullopt, - std::nullopt}, - {tfOneAssetLPToken, - std::nullopt, - XRP(100), - USD(100), - std::nullopt, - std::nullopt}, - {tfOneAssetLPToken, - std::nullopt, - XRP(100), - std::nullopt, - STAmount{USD, 1, -1}, - std::nullopt}, - {tfOneAssetLPToken, 1'000, XRP(100), std::nullopt, std::nullopt, 1'000}, - {tfLimitLPToken, 1'000, std::nullopt, std::nullopt, std::nullopt, std::nullopt}, - {tfLimitLPToken, 1'000, USD(100), std::nullopt, std::nullopt, std::nullopt}, - {tfLimitLPToken, std::nullopt, USD(100), XRP(100), std::nullopt, std::nullopt}, - {tfLimitLPToken, - std::nullopt, - XRP(100), - std::nullopt, - STAmount{USD, 1, -1}, - 1'000}, - {tfTwoAssetIfEmpty, - std::nullopt, - std::nullopt, - std::nullopt, - std::nullopt, - 1'000}, - {tfTwoAssetIfEmpty, - 1'000, - std::nullopt, - std::nullopt, - std::nullopt, - std::nullopt}, - {tfTwoAssetIfEmpty, - std::nullopt, - XRP(100), - USD(100), - STAmount{USD, 1, -1}, - std::nullopt}, - {tfTwoAssetIfEmpty | tfLPToken, - std::nullopt, - XRP(100), - USD(100), - STAmount{USD, 1, -1}, - std::nullopt}}; + std::optional>> const invalidOptions = { + // flags, tokens, asset1In, asset2in, EPrice, tfee + {tfLPToken, 1'000, std::nullopt, USD(100), std::nullopt, std::nullopt}, + {tfLPToken, 1'000, XRP(100), std::nullopt, std::nullopt, std::nullopt}, + {tfLPToken, 1'000, std::nullopt, std::nullopt, STAmount{USD, 1, -1}, std::nullopt}, + {tfLPToken, + std::nullopt, + USD(100), + std::nullopt, + STAmount{USD, 1, -1}, + std::nullopt}, + {tfLPToken, 1'000, XRP(100), std::nullopt, STAmount{USD, 1, -1}, std::nullopt}, + {tfLPToken, 1'000, std::nullopt, std::nullopt, std::nullopt, 1'000}, + {tfSingleAsset, 1'000, std::nullopt, std::nullopt, std::nullopt, std::nullopt}, + {tfSingleAsset, std::nullopt, std::nullopt, USD(100), std::nullopt, std::nullopt}, + {tfSingleAsset, + std::nullopt, + std::nullopt, + std::nullopt, + STAmount{USD, 1, -1}, + std::nullopt}, + {tfSingleAsset, std::nullopt, USD(100), std::nullopt, std::nullopt, 1'000}, + {tfTwoAsset, 1'000, std::nullopt, std::nullopt, std::nullopt, std::nullopt}, + {tfTwoAsset, std::nullopt, XRP(100), USD(100), STAmount{USD, 1, -1}, std::nullopt}, + {tfTwoAsset, std::nullopt, XRP(100), std::nullopt, std::nullopt, std::nullopt}, + {tfTwoAsset, std::nullopt, XRP(100), USD(100), std::nullopt, 1'000}, + {tfTwoAsset, + std::nullopt, + std::nullopt, + USD(100), + STAmount{USD, 1, -1}, + std::nullopt}, + {tfOneAssetLPToken, 1'000, std::nullopt, std::nullopt, std::nullopt, std::nullopt}, + {tfOneAssetLPToken, std::nullopt, XRP(100), USD(100), std::nullopt, std::nullopt}, + {tfOneAssetLPToken, + std::nullopt, + XRP(100), + std::nullopt, + STAmount{USD, 1, -1}, + std::nullopt}, + {tfOneAssetLPToken, 1'000, XRP(100), std::nullopt, std::nullopt, 1'000}, + {tfLimitLPToken, 1'000, std::nullopt, std::nullopt, std::nullopt, std::nullopt}, + {tfLimitLPToken, 1'000, USD(100), std::nullopt, std::nullopt, std::nullopt}, + {tfLimitLPToken, std::nullopt, USD(100), XRP(100), std::nullopt, std::nullopt}, + {tfLimitLPToken, std::nullopt, XRP(100), std::nullopt, STAmount{USD, 1, -1}, 1'000}, + {tfTwoAssetIfEmpty, std::nullopt, std::nullopt, std::nullopt, std::nullopt, 1'000}, + {tfTwoAssetIfEmpty, 1'000, std::nullopt, std::nullopt, std::nullopt, std::nullopt}, + {tfTwoAssetIfEmpty, + std::nullopt, + XRP(100), + USD(100), + STAmount{USD, 1, -1}, + std::nullopt}, + {tfTwoAssetIfEmpty | tfLPToken, + std::nullopt, + XRP(100), + USD(100), + STAmount{USD, 1, -1}, + std::nullopt}}; for (auto const& it : invalidOptions) { ammAlice.deposit( @@ -739,16 +699,20 @@ private: [&](AMM& ammAlice, Env& env) { env(fset(gw, asfGlobalFreeze)); if (!features[featureAMMClawback]) + { // If the issuer set global freeze, the holder still can // deposit the other non-frozen token when AMMClawback is // not enabled. ammAlice.deposit(carol, XRP(100)); + } else + { // If the issuer set global freeze, the holder cannot // deposit the other non-frozen token when AMMClawback is // enabled. ammAlice.deposit( carol, XRP(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN)); + } ammAlice.deposit( carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN)); ammAlice.deposit(carol, 1'000'000, std::nullopt, std::nullopt, ter(tecFROZEN)); @@ -766,14 +730,18 @@ private: env(trust(gw, carol["USD"](0), tfSetFreeze)); env.close(); if (!features[featureAMMClawback]) + { // Can deposit non-frozen token if AMMClawback is not // enabled ammAlice.deposit(carol, XRP(100)); + } else + { // Cannot deposit non-frozen token if the other token is // frozen when AMMClawback is enabled ammAlice.deposit( carol, XRP(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN)); + } ammAlice.deposit(carol, 1'000'000, std::nullopt, std::nullopt, ter(tecFROZEN)); ammAlice.deposit( @@ -837,14 +805,18 @@ private: env.close(); if (features[featureAMMClawback]) + { // if featureAMMClawback is enabled, bob can not deposit XRP // because he's not authorized to hold the paired token // gw["USD"]. amm.deposit( bob, XRP(10), std::nullopt, std::nullopt, std::nullopt, ter(tecNO_AUTH)); + } else + { amm.deposit( bob, XRP(10), std::nullopt, std::nullopt, std::nullopt, ter(tesSUCCESS)); + } } // Insufficient XRP balance @@ -1432,7 +1404,7 @@ private: testAMM( [&](AMM& ammAlice, Env& env) { - WithdrawArg args{ + WithdrawArg const args{ .asset1Out = XRP(100), .err = ter(tecAMM_BALANCE), }; @@ -1442,7 +1414,7 @@ private: testAMM( [&](AMM& ammAlice, Env& env) { - WithdrawArg args{ + WithdrawArg const args{ .asset1Out = USD(100), .err = ter(tecAMM_BALANCE), }; @@ -1465,7 +1437,7 @@ private: env(pay(gw, alice, USD(10'000))); env.close(); AMM ammAlice(env, alice, XRP(10'000), USD(10'000)); - WithdrawArg args{ + WithdrawArg const args{ .account = bob, .asset1Out = USD(100), .err = ter(tecNO_AUTH), @@ -1503,79 +1475,58 @@ private: std::optional, std::optional, std::optional, - NotTEC>> - invalidOptions = { - // tokens, asset1Out, asset2Out, EPrice, flags, ter - {std::nullopt, - std::nullopt, - std::nullopt, - std::nullopt, - std::nullopt, - temMALFORMED}, - {std::nullopt, - std::nullopt, - std::nullopt, - std::nullopt, - tfSingleAsset | tfTwoAsset, - temMALFORMED}, - {1'000, std::nullopt, std::nullopt, std::nullopt, tfWithdrawAll, temMALFORMED}, - {std::nullopt, - USD(0), - XRP(100), - std::nullopt, - tfWithdrawAll | tfLPToken, - temMALFORMED}, - {std::nullopt, - std::nullopt, - USD(100), - std::nullopt, - tfWithdrawAll, - temMALFORMED}, - {std::nullopt, - std::nullopt, - std::nullopt, - std::nullopt, - tfWithdrawAll | tfOneAssetWithdrawAll, - temMALFORMED}, - {std::nullopt, - USD(100), - std::nullopt, - std::nullopt, - tfWithdrawAll, - temMALFORMED}, - {std::nullopt, - std::nullopt, - std::nullopt, - std::nullopt, - tfOneAssetWithdrawAll, - temMALFORMED}, - {1'000, std::nullopt, USD(100), std::nullopt, std::nullopt, temMALFORMED}, - {std::nullopt, - std::nullopt, - std::nullopt, - IOUAmount{250, 0}, - tfWithdrawAll, - temMALFORMED}, - {1'000, - std::nullopt, - std::nullopt, - IOUAmount{250, 0}, - std::nullopt, - temMALFORMED}, - {std::nullopt, - std::nullopt, - USD(100), - IOUAmount{250, 0}, - std::nullopt, - temMALFORMED}, - {std::nullopt, - XRP(100), - USD(100), - IOUAmount{250, 0}, - std::nullopt, - temMALFORMED}, - {1'000, XRP(100), USD(100), std::nullopt, std::nullopt, temMALFORMED}, - {std::nullopt, XRP(100), USD(100), std::nullopt, tfWithdrawAll, temMALFORMED}}; + NotTEC>> const invalidOptions = { + // tokens, asset1Out, asset2Out, EPrice, flags, ter + {std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + temMALFORMED}, + {std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + tfSingleAsset | tfTwoAsset, + temMALFORMED}, + {1'000, std::nullopt, std::nullopt, std::nullopt, tfWithdrawAll, temMALFORMED}, + {std::nullopt, + USD(0), + XRP(100), + std::nullopt, + tfWithdrawAll | tfLPToken, + temMALFORMED}, + {std::nullopt, std::nullopt, USD(100), std::nullopt, tfWithdrawAll, temMALFORMED}, + {std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + tfWithdrawAll | tfOneAssetWithdrawAll, + temMALFORMED}, + {std::nullopt, USD(100), std::nullopt, std::nullopt, tfWithdrawAll, temMALFORMED}, + {std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + tfOneAssetWithdrawAll, + temMALFORMED}, + {1'000, std::nullopt, USD(100), std::nullopt, std::nullopt, temMALFORMED}, + {std::nullopt, + std::nullopt, + std::nullopt, + IOUAmount{250, 0}, + tfWithdrawAll, + temMALFORMED}, + {1'000, std::nullopt, std::nullopt, IOUAmount{250, 0}, std::nullopt, temMALFORMED}, + {std::nullopt, + std::nullopt, + USD(100), + IOUAmount{250, 0}, + std::nullopt, + temMALFORMED}, + {std::nullopt, XRP(100), USD(100), IOUAmount{250, 0}, std::nullopt, temMALFORMED}, + {1'000, XRP(100), USD(100), std::nullopt, std::nullopt, temMALFORMED}, + {std::nullopt, XRP(100), USD(100), std::nullopt, tfWithdrawAll, temMALFORMED}}; for (auto const& it : invalidOptions) { ammAlice.withdraw( @@ -1687,8 +1638,10 @@ private: ammAlice.withdraw( alice, IOUAmount{9'999'999'9999, -4}, std::nullopt, std::nullopt, err); if (env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount(1), STAmount{USD, 1, -7}, IOUAmount{1, -4})); + } }, std::nullopt, 0, @@ -1707,8 +1660,10 @@ private: ammAlice.withdraw( alice, IOUAmount{9'999'999'999999999, -9}, std::nullopt, std::nullopt, err); if (env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount(1), STAmount{USD, 1, -11}, IOUAmount{1, -8})); + } }, std::nullopt, 0, @@ -1901,7 +1856,7 @@ private: BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount(beast::Zero()))); BEAST_EXPECT(expectHolding(env, carol, USD(30'000))); BEAST_EXPECT( - expectLedgerEntryRoot(env, carol, XRPAmount{30'000'000'000 - 2 * baseFee})); + expectLedgerEntryRoot(env, carol, XRPAmount{30'000'000'000 - (2 * baseFee)})); }); // Equal withdrawal by tokens 1000000, 10% @@ -1932,11 +1887,15 @@ private: [&](AMM& ammAlice, Env& env) { ammAlice.withdraw(alice, XRP(1'000)); if (!env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRP(9'000), USD(10'000), IOUAmount{9'486'832'98050514, -8})); + } else + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount{9'000'000'001}, USD(10'000), IOUAmount{9'486'832'98050514, -8})); + } }, std::nullopt, 0, @@ -1953,7 +1912,7 @@ private: // Withdraw all tokens. testAMM([&](AMM& ammAlice, Env& env) { env(trust(carol, STAmount{ammAlice.lptIssue(), 10'000})); - // Can SetTrust only for AMM LP tokens + // Can TrustSet only for AMM LP tokens env(trust(carol, STAmount{Issue{EUR.currency, ammAlice.ammAccount()}, 10'000}), ter(tecNO_PERMISSION)); env.close(); @@ -1963,7 +1922,7 @@ private: BEAST_EXPECT(!env.le(keylet::ownerDir(ammAlice.ammAccount()))); // Can create AMM for the XRP/USD pair - AMM ammCarol(env, carol, XRP(10'000), USD(10'000)); + AMM const ammCarol(env, carol, XRP(10'000), USD(10'000)); BEAST_EXPECT( ammCarol.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0})); }); @@ -2000,11 +1959,15 @@ private: lpTokens = ammAlice.deposit(carol, XRPAmount(1)); ammAlice.withdraw(carol, lpTokens, XRPAmount(0)); if (!env.enabled(fixAMMv1_3)) + { BEAST_EXPECT( ammAlice.expectBalances(XRP(10'000), USD(10'000), ammAlice.tokens())); + } else + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount(10'000'000'001), USD(10'000), ammAlice.tokens())); + } BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0})); }, std::nullopt, @@ -2057,20 +2020,26 @@ private: ammAlice.withdraw(carol, USD(100), std::nullopt, IOUAmount{520, 0}); BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{153'846'15384616, -8})); if (!env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount(11'000'000'000), STAmount{USD, UINT64_C(9'372'781065088757), -12}, IOUAmount{10'153'846'15384616, -8})); + } else if (env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount(11'000'000'000), STAmount{USD, UINT64_C(9'372'781065088769), -12}, IOUAmount{10'153'846'15384616, -8})); + } else if (env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount(11'000'000'000), STAmount{USD, UINT64_C(9'372'78106508877), -11}, IOUAmount{10'153'846'15384616, -8})); + } ammAlice.withdrawAll(carol); BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0})); }, @@ -2083,20 +2052,26 @@ private: ammAlice.withdraw(carol, USD(0), std::nullopt, IOUAmount{520, 0}); BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{153'846'15384616, -8})); if (!env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRP(11'000), STAmount{USD, UINT64_C(9'372'781065088757), -12}, IOUAmount{10'153'846'15384616, -8})); + } else if (env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRP(11'000), STAmount{USD, UINT64_C(9'372'781065088769), -12}, IOUAmount{10'153'846'15384616, -8})); + } else if (env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRP(11'000), STAmount{USD, UINT64_C(9'372'78106508877), -11}, IOUAmount{10'153'846'15384616, -8})); + } }, std::nullopt, 0, @@ -2142,11 +2117,15 @@ private: // Single XRP pool ammAlice.withdraw(alice, std::nullopt, XRPAmount{1}); if (!env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount{9'999'999'999}, USD(10'000), IOUAmount{9'999'999'9995, -4})); + } else + { BEAST_EXPECT(ammAlice.expectBalances( XRP(10'000), USD(10'000), IOUAmount{9'999'999'9995, -4})); + } }, std::nullopt, 0, @@ -2670,11 +2649,15 @@ private: fund(env, gw, {bob}, {USD(10'000)}, Fund::Acct); ammAlice.deposit(bob, 1'000'000); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances( XRP(12'000), USD(12'000), IOUAmount{12'000'000, 0})); + } else + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount{12'000'000'001}, USD(12'000), IOUAmount{12'000'000, 0})); + } // Initial state. Pay bidMin. env(ammAlice.bid({.account = carol, .bidMin = 110})).close(); @@ -2687,13 +2670,13 @@ private: // 10th Interval after close, price for 1st interval. env(ammAlice.bid({.account = carol})); - env.close(seconds(10 * AUCTION_SLOT_INTERVAL_DURATION + 1)); + env.close(seconds((10 * AUCTION_SLOT_INTERVAL_DURATION) + 1)); BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 10, IOUAmount{121'275, -3})); // 20th Interval (expired) after close, price for 10th interval. env(ammAlice.bid({.account = bob})); env.close( - seconds(AUCTION_SLOT_TIME_INTERVALS * AUCTION_SLOT_INTERVAL_DURATION + 1)); + seconds((AUCTION_SLOT_TIME_INTERVALS * AUCTION_SLOT_INTERVAL_DURATION) + 1)); BEAST_EXPECT(ammAlice.expectAuctionSlot(0, std::nullopt, IOUAmount{127'33875, -5})); // 0 Interval. @@ -2701,11 +2684,15 @@ private: BEAST_EXPECT(ammAlice.expectAuctionSlot(0, std::nullopt, IOUAmount{110})); // ~321.09 tokens burnt on bidding fees. if (!features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances( XRP(12'000), USD(12'000), IOUAmount{11'999'678'91, -2})); + } else + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount{12'000'000'001}, USD(12'000), IOUAmount{11'999'678'91, -2})); + } }, std::nullopt, 0, @@ -2735,10 +2722,14 @@ private: ammTokens -= slotPrice; BEAST_EXPECT(ammAlice.expectAuctionSlot(100, 0, slotPrice)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances(XRP(13'000), USD(13'000), ammTokens)); + } else + { BEAST_EXPECT( ammAlice.expectBalances(XRPAmount{13'000'000'003}, USD(13'000), ammTokens)); + } // Discounted trade for (int i = 0; i < 10; ++i) { @@ -2774,15 +2765,19 @@ private: env.balance(ed, USD) == STAmount(USD, UINT64_C(18'999'0057261184), -10)); // USD pool is slightly higher because of the fees. if (!features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances( XRP(13'000), STAmount(USD, UINT64_C(13'002'98282151422), -11), ammTokens)); + } else + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount{13'000'000'003}, STAmount(USD, UINT64_C(13'002'98282151422), -11), ammTokens)); + } } ammTokens = ammAlice.getLPTokensBalance(); // Trade with the fee @@ -2818,51 +2813,67 @@ private: else { if (!features[fixAMMv1_3]) + { BEAST_EXPECT( env.balance(dan, USD) == STAmount(USD, UINT64_C(19'490'05672274399), -11)); + } else + { BEAST_EXPECT( env.balance(dan, USD) == STAmount(USD, UINT64_C(19'490'05672274398), -11)); + } // USD pool gains more in dan's fees. if (!features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances( XRP(13'000), STAmount{USD, UINT64_C(13'012'92609877023), -11}, ammTokens)); + } else + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount{13'000'000'003}, STAmount{USD, UINT64_C(13'012'92609877024), -11}, ammTokens)); + } // Discounted fee payment ammAlice.deposit(carol, USD(100)); ammTokens = ammAlice.getLPTokensBalance(); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances( XRP(13'000), STAmount{USD, UINT64_C(13'112'92609877023), -11}, ammTokens)); + } else + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount{13'000'000'003}, STAmount{USD, UINT64_C(13'112'92609877024), -11}, ammTokens)); + } env(pay(carol, bob, USD(100)), path(~USD), sendmax(XRP(110))); env.close(); // carol pays 100000 drops in fees // 99900668XRP swapped in for 100USD if (!features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount{13'100'000'668}, STAmount{USD, UINT64_C(13'012'92609877023), -11}, ammTokens)); + } else + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount{13'100'000'671}, STAmount{USD, UINT64_C(13'012'92609877024), -11}, ammTokens)); + } } // Payment with the trading fee env(pay(alice, carol, XRP(100)), path(~XRP), sendmax(USD(110))); @@ -2896,13 +2907,17 @@ private: // clock is parent's based env.close(); if (!features[fixAMMv1_1]) + { BEAST_EXPECT( env.balance(carol, USD) == STAmount(USD, UINT64_C(29'399'00572620545), -11)); + } else if (!features[fixAMMv1_3]) + { BEAST_EXPECT( env.balance(carol, USD) == STAmount(USD, UINT64_C(29'399'00572620544), -11)); + } ammTokens = ammAlice.getLPTokensBalance(); for (int i = 0; i < 10; ++i) { @@ -3070,12 +3085,12 @@ private: fund(env, gw, {alice, bob}, XRP(2'000), {USD(2'000)}); AMM amm(env, gw, XRP(1'000), USD(1'010), false, 1'000); - Json::Value tx = amm.bid({.account = alice, .bidMin = 500}); + Json::Value const tx = amm.bid({.account = alice, .bidMin = 500}); { auto jtx = env.jt(tx, seq(1), fee(baseFee)); env.app().config().features.erase(featureAMM); - PreflightContext pfCtx( + PreflightContext const pfCtx( env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal); auto pf = Transactor::invokePreflight(pfCtx); BEAST_EXPECT(pf == temDISABLED); @@ -3086,10 +3101,10 @@ private: auto jtx = env.jt(tx, seq(1), fee(baseFee)); jtx.jv["TxnSignature"] = "deadbeef"; jtx.stx = env.ust(jtx); - PreflightContext pfCtx( + PreflightContext const pfCtx( env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal); auto pf = Transactor::invokePreflight(pfCtx); - BEAST_EXPECT(pf != tesSUCCESS); + BEAST_EXPECT(!isTesSuccess(pf)); } { @@ -3097,7 +3112,7 @@ private: jtx.jv["Asset2"]["currency"] = "XRP"; jtx.jv["Asset2"].removeMember("issuer"); jtx.stx = env.ust(jtx); - PreflightContext pfCtx( + PreflightContext const pfCtx( env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal); auto pf = Transactor::invokePreflight(pfCtx); BEAST_EXPECT(pf == temBAD_AMM_TOKENS); @@ -3121,7 +3136,7 @@ private: Env env(*this); fund(env, gw, {alice, carol}, XRP(1'000), {USD(100)}); // XRP balance is below reserve - AMM ammAlice(env, acct, XRP(10), USD(10)); + AMM const ammAlice(env, acct, XRP(10), USD(10)); // Pay below reserve env(pay(carol, ammAlice.ammAccount(), XRP(10)), ter(tecNO_PERMISSION)); // Pay above reserve @@ -3133,7 +3148,7 @@ private: Env env(*this); fund(env, gw, {alice, carol}, XRP(10'000'000), {USD(10'000)}); // XRP balance is above reserve - AMM ammAlice(env, acct, XRP(1'000'000), USD(100)); + AMM const ammAlice(env, acct, XRP(1'000'000), USD(100)); // Pay below reserve env(pay(carol, ammAlice.ammAccount(), XRP(10)), ter(tecNO_PERMISSION)); // Pay above reserve @@ -3558,7 +3573,7 @@ private: env.close(); env(offer(bob, XRP(50), USD(150)), txflags(tfPassive)); env.close(); - AMM ammAlice(env, alice, XRP(1'000), USD(1'050)); + AMM const ammAlice(env, alice, XRP(1'000), USD(1'050)); env(pay(alice, carol, USD(200)), sendmax(XRP(200)), txflags(tfPartialPayment)); env.close(); BEAST_EXPECT(ammAlice.expectBalances(XRP(1'050), USD(1'000), ammAlice.tokens())); @@ -3885,13 +3900,13 @@ private: XRP(100'000), {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)}); fund(env, gw, {carol, bob}, XRP(1'000), {USD(200)}, Fund::Acct); - AMM xrp_eur(env, alice, XRP(10'100), EUR(10'000)); - AMM eur_btc(env, alice, EUR(10'000), BTC(10'200)); - AMM btc_usd(env, alice, BTC(10'100), USD(10'000)); - AMM xrp_usd(env, alice, XRP(10'150), USD(10'200)); - AMM xrp_eth(env, alice, XRP(10'000), ETH(10'100)); - AMM eth_eur(env, alice, ETH(10'900), EUR(11'000)); - AMM eur_usd(env, alice, EUR(10'100), USD(10'000)); + AMM const xrp_eur(env, alice, XRP(10'100), EUR(10'000)); + AMM const eur_btc(env, alice, EUR(10'000), BTC(10'200)); + AMM const btc_usd(env, alice, BTC(10'100), USD(10'000)); + AMM const xrp_usd(env, alice, XRP(10'150), USD(10'200)); + AMM const xrp_eth(env, alice, XRP(10'000), ETH(10'100)); + AMM const eth_eur(env, alice, ETH(10'900), EUR(11'000)); + AMM const eur_usd(env, alice, EUR(10'100), USD(10'000)); env(pay(bob, carol, USD(100)), path(~EUR, ~BTC, ~USD), path(~USD), @@ -3966,11 +3981,11 @@ private: XRP(40'000), {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)}); fund(env, gw, {carol, bob}, XRP(1000), {USD(200)}, Fund::Acct); - AMM xrp_eur(env, alice, XRP(10'100), EUR(10'000)); - AMM eur_btc(env, alice, EUR(10'000), BTC(10'200)); - AMM btc_usd(env, alice, BTC(10'100), USD(10'000)); - AMM xrp_eth(env, alice, XRP(10'000), ETH(10'100)); - AMM eth_eur(env, alice, ETH(10'900), EUR(11'000)); + AMM const xrp_eur(env, alice, XRP(10'100), EUR(10'000)); + AMM const eur_btc(env, alice, EUR(10'000), BTC(10'200)); + AMM const btc_usd(env, alice, BTC(10'100), USD(10'000)); + AMM const xrp_eth(env, alice, XRP(10'000), ETH(10'100)); + AMM const eth_eur(env, alice, ETH(10'900), EUR(11'000)); env(pay(bob, carol, USD(100)), path(~EUR, ~BTC, ~USD), path(~ETH, ~EUR, ~BTC, ~USD), @@ -4034,7 +4049,7 @@ private: fund(env, gw, {bob}, {EUR(400)}, Fund::IOUOnly); env(trust(alice, EUR(200))); for (int i = 0; i < 30; ++i) - env(offer(alice, EUR(1.0 + 0.01 * i), XRP(1))); + env(offer(alice, EUR(1.0 + (0.01 * i)), XRP(1))); // This is worse quality offer than 30 offers above. // It will not be consumed because of AMM offers limit. env(offer(alice, EUR(140), XRP(100))); @@ -4074,7 +4089,7 @@ private: fund(env, gw, {bob}, {EUR(400)}, Fund::IOUOnly); env(trust(alice, EUR(200))); for (int i = 0; i < 29; ++i) - env(offer(alice, EUR(1.0 + 0.01 * i), XRP(1))); + env(offer(alice, EUR(1.0 + (0.01 * i)), XRP(1))); // This is worse quality offer than 30 offers above. // It will not be consumed because of AMM offers limit. env(offer(alice, EUR(140), XRP(100))); @@ -4111,7 +4126,7 @@ private: Env env(*this, features); fund(env, gw, {alice, carol, bob}, XRP(30'000), {USD(30'000)}); env(offer(bob, XRP(100), USD(100.001))); - AMM ammAlice(env, alice, XRP(10'000), USD(10'100)); + AMM const ammAlice(env, alice, XRP(10'000), USD(10'100)); env(offer(carol, USD(100), XRP(100))); if (!features[fixAMMv1_1]) { @@ -4191,7 +4206,7 @@ private: env(ammAlice.bid({.account = carol, .bidMin = 100})); BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{4'999'900})); BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{100})); - BEAST_EXPECT(accountBalance(env, carol) == std::to_string(22500000000 - 4 * baseFee)); + BEAST_EXPECT(accountBalance(env, carol) == std::to_string(22500000000 - (4 * baseFee))); priceXRP = ammAssetOut( STAmount{XRPAmount{10'000'000'000}}, STAmount{token1, 9'999'900}, @@ -4199,7 +4214,7 @@ private: 0); // Carol withdraws ammAlice.withdrawAll(carol, XRP(0)); - BEAST_EXPECT(accountBalance(env, carol) == std::to_string(29999949999 - 5 * baseFee)); + BEAST_EXPECT(accountBalance(env, carol) == std::to_string(29999949999 - (5 * baseFee))); BEAST_EXPECT(ammAlice.expectBalances( XRPAmount{10'000'000'000} - priceXRP, USD(10'000), IOUAmount{5'000'000})); BEAST_EXPECT(ammAlice.expectLPTokens(alice, IOUAmount{5'000'000})); @@ -4336,10 +4351,10 @@ private: env.trust(TSTB(10'000), C); env(pay(A, C, TSTA(10'000))); env(pay(B, C, TSTB(10'000))); - AMM amm(env, C, TSTA(5'000), TSTB(5'000)); + AMM const amm(env, C, TSTA(5'000), TSTB(5'000)); auto const ammIss = Issue(TSTA.currency, amm.ammAccount()); - // Can SetTrust only for AMM LP tokens + // Can TrustSet only for AMM LP tokens env(trust(D, STAmount{ammIss, 10'000}), ter(tecNO_PERMISSION)); env.close(); @@ -4386,7 +4401,7 @@ private: std::string lp2TakerPays; // Execute with AMM first prep( - [&](Env& env) { AMM amm(env, LP1, TST(25), XRP(250)); }, + [&](Env& env) { AMM const amm(env, LP1, TST(25), XRP(250)); }, [&](Env& env) { lp2TSTBalance = getAccountLines(env, LP2, TST)["lines"][0u]["balance"].asString(); auto const offer = getAccountOffers(env, LP2)["offers"][0u]; @@ -4397,17 +4412,21 @@ private: prep( [&](Env& env) { if (!features[fixAMMv1_1]) + { env(offer( LP1, XRPAmount{18'095'133}, STAmount{TST, UINT64_C(1'68737984885388), -14}), txflags(tfPassive)); + } else + { env(offer( LP1, XRPAmount{18'095'132}, STAmount{TST, UINT64_C(1'68737976189735), -14}), txflags(tfPassive)); + } }, [&](Env& env) { BEAST_EXPECT( @@ -4528,11 +4547,15 @@ private: // carol withdraws ~1,443.44USD auto const balanceAfterWithdraw = [&]() { if (!features[fixAMMv1_1] && !features[fixAMMv1_3]) + { return STAmount(USD, UINT64_C(30'443'43891402715), -11); - else if (features[fixAMMv1_1] && !features[fixAMMv1_3]) + } + if (features[fixAMMv1_1] && !features[fixAMMv1_3]) + { return STAmount(USD, UINT64_C(30'443'43891402714), -11); - else - return STAmount(USD, UINT64_C(30'443'43891402713), -11); + } + + return STAmount(USD, UINT64_C(30'443'43891402713), -11); }(); BEAST_EXPECT(env.balance(carol, USD) == balanceAfterWithdraw); // Set to original pool size @@ -4543,25 +4566,37 @@ private: BEAST_EXPECT(ammAlice.expectTradingFee(0)); auto const tokensNoFee = ammAlice.withdraw(carol, deposit); if (!features[fixAMMv1_1] && !features[fixAMMv1_3]) + { BEAST_EXPECT( env.balance(carol, USD) == STAmount(USD, UINT64_C(30'443'43891402717), -11)); + } else if (features[fixAMMv1_1] && !features[fixAMMv1_3]) + { BEAST_EXPECT( env.balance(carol, USD) == STAmount(USD, UINT64_C(30'443'43891402716), -11)); + } else + { BEAST_EXPECT( env.balance(carol, USD) == STAmount(USD, UINT64_C(30'443'43891402713), -11)); + } // carol pays ~4008 LPTokens in fees or ~0.5% of the no-fee // LPTokens if (!features[fixAMMv1_1] && !features[fixAMMv1_3]) + { BEAST_EXPECT(tokensNoFee == IOUAmount(746'579'80779913, -8)); + } else if (features[fixAMMv1_1] && !features[fixAMMv1_3]) + { BEAST_EXPECT(tokensNoFee == IOUAmount(746'579'80779912, -8)); + } else + { BEAST_EXPECT(tokensNoFee == IOUAmount(746'579'80779911, -8)); + } BEAST_EXPECT(tokensFee == IOUAmount(750'588'23529411, -8)); }, std::nullopt, @@ -4670,7 +4705,7 @@ private: Account const ed("ed"); fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(2'000), EUR(2'000)}); env(offer(carol, EUR(5), USD(5))); - AMM ammAlice(env, alice, USD(1'005), EUR(1'000)); + AMM const ammAlice(env, alice, USD(1'005), EUR(1'000)); env(pay(bob, ed, USD(10)), path(~USD), sendmax(EUR(15)), txflags(tfNoRippleDirect)); BEAST_EXPECT(expectHolding(env, ed, USD(2'010))); if (!features[fixAMMv1_1]) @@ -4698,7 +4733,7 @@ private: fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(2'000), EUR(2'000)}); env(offer(carol, EUR(5), USD(5))); // Set 0.25% fee - AMM ammAlice(env, alice, USD(1'005), EUR(1'000), false, 250); + AMM const ammAlice(env, alice, USD(1'005), EUR(1'000), false, 250); env(pay(bob, ed, USD(10)), path(~USD), sendmax(EUR(15)), txflags(tfNoRippleDirect)); BEAST_EXPECT(expectHolding(env, ed, USD(2'010))); if (!features[fixAMMv1_1]) @@ -4731,7 +4766,7 @@ private: fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(2'000), EUR(2'000)}); env(offer(carol, EUR(10), USD(10))); // Set 1% fee - AMM ammAlice(env, alice, USD(1'005), EUR(1'000), false, 1'000); + AMM const ammAlice(env, alice, USD(1'005), EUR(1'000), false, 1'000); env(pay(bob, ed, USD(10)), path(~USD), sendmax(EUR(15)), txflags(tfNoRippleDirect)); BEAST_EXPECT(expectHolding(env, ed, USD(2'010))); BEAST_EXPECT(expectHolding(env, bob, EUR(1'990))); @@ -4749,7 +4784,7 @@ private: fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(2'000), EUR(2'000)}); env(offer(carol, EUR(9), USD(9))); // Set 1% fee - AMM ammAlice(env, alice, USD(1'005), EUR(1'000), false, 1'000); + AMM const ammAlice(env, alice, USD(1'005), EUR(1'000), false, 1'000); env(pay(bob, ed, USD(10)), path(~USD), sendmax(EUR(15)), txflags(tfNoRippleDirect)); BEAST_EXPECT(expectHolding(env, ed, USD(2'010))); BEAST_EXPECT(expectHolding(env, bob, STAmount{EUR, UINT64_C(1'989'993923296712), -12})); @@ -4808,49 +4843,73 @@ private: // other have a tiny loss. The last account to withdraw // gets everything in the pool. if (!features[fixAMMv1_1] && !features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances( XRP(10'000), STAmount{USD, UINT64_C(10'000'0000000013), -10}, IOUAmount{10'000'000})); + } else if (features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances( XRP(10'000), STAmount{USD, UINT64_C(10'000'0000000003), -10}, IOUAmount{10'000'000})); + } else + { BEAST_EXPECT( ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000})); + } BEAST_EXPECT(expectHolding(env, ben, USD(1'500'000))); BEAST_EXPECT(expectHolding(env, simon, USD(1'500'000))); BEAST_EXPECT(expectHolding(env, chris, USD(1'500'000))); BEAST_EXPECT(expectHolding(env, dan, USD(1'500'000))); if (!features[fixAMMv1_1] && !features[fixAMMv1_3]) + { BEAST_EXPECT(expectHolding( env, carol, STAmount{USD, UINT64_C(30'000'00000000001), -11})); + } else if (features[fixAMMv1_1] && !features[fixAMMv1_3]) + { BEAST_EXPECT(expectHolding(env, carol, USD(30'000))); + } else + { BEAST_EXPECT(expectHolding(env, carol, USD(30'000))); + } BEAST_EXPECT(expectHolding(env, ed, USD(1'500'000))); BEAST_EXPECT(expectHolding(env, paul, USD(1'500'000))); if (!features[fixAMMv1_1] && !features[fixAMMv1_3]) + { BEAST_EXPECT(expectHolding( env, natalie, STAmount{USD, UINT64_C(1'500'000'000000002), -9})); + } else if (features[fixAMMv1_1] && !features[fixAMMv1_3]) + { BEAST_EXPECT(expectHolding( env, natalie, STAmount{USD, UINT64_C(1'500'000'000000005), -9})); + } else + { BEAST_EXPECT(expectHolding(env, natalie, USD(1'500'000))); + } ammAlice.withdrawAll(alice); BEAST_EXPECT(!ammAlice.ammExists()); if (!features[fixAMMv1_1]) + { BEAST_EXPECT( expectHolding(env, alice, STAmount{USD, UINT64_C(30'000'0000000013), -10})); + } else if (features[fixAMMv1_3]) + { BEAST_EXPECT( expectHolding(env, alice, STAmount{USD, UINT64_C(30'000'0000000003), -10})); + } else + { BEAST_EXPECT(expectHolding(env, alice, USD(30'000))); + } // alice XRP balance is 30,000 initial - 50 AMMCreate fee - // 10drops fee BEAST_EXPECT( @@ -4919,7 +4978,7 @@ private: // 30,000 initial - (deposit+withdraw) * 10 BEAST_EXPECT( accountBalance(env, carol) == - std::to_string(30'000'000'000 - 20 * baseFee)); + std::to_string(30'000'000'000 - (20 * baseFee))); BEAST_EXPECT(accountBalance(env, ed) == xrpBalance); BEAST_EXPECT(accountBalance(env, paul) == xrpBalance); BEAST_EXPECT(accountBalance(env, natalie) == xrpBalance); @@ -4943,7 +5002,7 @@ private: BEAST_EXPECT(accountBalance(env, dan) == xrpBalanceText); BEAST_EXPECT( accountBalance(env, carol) == - std::to_string(30'000'000'000 - 20 * baseFee - 10)); + std::to_string(30'000'000'000 - (20 * baseFee) - 10)); BEAST_EXPECT(accountBalance(env, ed) == (xrpBalance + drops(2)).getText()); BEAST_EXPECT(accountBalance(env, paul) == (xrpBalance + drops(3)).getText()); BEAST_EXPECT(accountBalance(env, natalie) == (xrpBalance + drops(5)).getText()); @@ -4988,7 +5047,7 @@ private: amm.withdrawAll(gw); BEAST_EXPECT(amm.ammExists()); - // Bid,Vote,Deposit,Withdraw,SetTrust failing with + // Bid,Vote,Deposit,Withdraw,TrustSet failing with // tecAMM_EMPTY. Deposit succeeds with tfTwoAssetIfEmpty option. env(amm.bid({ .account = alice, @@ -5034,7 +5093,7 @@ private: all); fund(env, gw, {alice}, XRP(20'000), {USD(10'000)}); AMM amm(env, gw, XRP(10'000), USD(10'000)); - for (auto i = 0; i < maxDeletableAMMTrustLines * 2 + 10; ++i) + for (auto i = 0; i < (maxDeletableAMMTrustLines * 2) + 10; ++i) { Account const a{std::to_string(i)}; env.fund(XRP(1'000), a); @@ -5066,7 +5125,7 @@ private: Env env(*this); env.fund(XRP(2'000), gw); env.fund(XRP(2'000), alice); - AMM amm(env, gw, XRP(1'000), USD(1'000)); + AMM const amm(env, gw, XRP(1'000), USD(1'000)); env(fset(gw, asfAllowTrustLineClawback), ter(tecOWNERS)); } @@ -5154,7 +5213,7 @@ private: // verify that the quality is better in the first case, and CLOB // is selected in the second case. { - std::array q; + std::array q{}; for (auto i = 0; i < 3; ++i) { Env env(*this, features); @@ -5227,7 +5286,7 @@ private: // Same as the payment but reduced offer quality { - std::array q; + std::array q{}; for (auto i = 0; i < 3; ++i) { Env env(*this, features); @@ -5252,6 +5311,7 @@ private: if (rates.first == 1.5) { if (!features[fixAMMv1_1]) + { BEAST_EXPECT(expectOffers( env, ed, @@ -5259,7 +5319,9 @@ private: {{Amounts{ STAmount{ETH, UINT64_C(378'6327949540823), -13}, STAmount{USD, UINT64_C(283'9745962155617), -13}}}})); + } else + { BEAST_EXPECT(expectOffers( env, ed, @@ -5267,10 +5329,12 @@ private: {{Amounts{ STAmount{ETH, UINT64_C(378'6327949540813), -13}, STAmount{USD, UINT64_C(283'974596215561), -12}}}})); + } } else { if (!features[fixAMMv1_1]) + { BEAST_EXPECT(expectOffers( env, ed, @@ -5278,7 +5342,9 @@ private: {{Amounts{ STAmount{ETH, UINT64_C(325'299461620749), -12}, STAmount{USD, UINT64_C(243'9745962155617), -13}}}})); + } else + { BEAST_EXPECT(expectOffers( env, ed, @@ -5286,6 +5352,7 @@ private: {{Amounts{ STAmount{ETH, UINT64_C(325'299461620748), -12}, STAmount{USD, UINT64_C(243'974596215561), -12}}}})); + } } } else if (i == 2) @@ -5426,7 +5493,7 @@ private: // multiple AMM offers are generated, which results in slightly // worse overall quality. { - std::array q; + std::array q{}; for (auto i = 0; i < 3; ++i) { Env env(*this, features); @@ -5608,7 +5675,7 @@ private: auto const xrpIouAmounts10_100 = TAmounts{XRPAmount{10}, IOUAmount{100}}; auto const iouXrpAmounts10_100 = TAmounts{IOUAmount{10}, XRPAmount{100}}; // clang-format off - std::vector> tests = { + std::vector> const tests = { //Pool In , Pool Out, Quality , Fee, Status {"0.001519763260828713", "1558701", Quality{5414253689393440221}, 1000, FailShouldSucceed}, {"0.01099814367603737", "1892611", Quality{5482264816516900274}, 1000, FailShouldSucceed}, @@ -5668,14 +5735,14 @@ private: }; // clang-format on - boost::regex rx("^\\d+$"); + boost::regex const rx("^\\d+$"); boost::smatch match; // tests that succeed should have the same amounts pre-fix and post-fix - std::vector> successAmounts; - Env env(*this, features, std::make_unique(&logs)); + std::vector> const successAmounts; + Env const env(*this, features, std::make_unique(&logs)); auto rules = env.current()->rules(); - CurrentTransactionRulesGuard rg(rules); - NumberMantissaScaleGuard sg(MantissaRange::small); + CurrentTransactionRulesGuard const rg(rules); + NumberMantissaScaleGuard const sg(MantissaRange::small); for (auto const& t : tests) { @@ -5701,18 +5768,26 @@ private: if (status == SucceedShouldSucceedResize) { if (!features[fixAMMv1_1]) + { BEAST_EXPECT(Quality{*amounts} < quality); + } else + { BEAST_EXPECT(Quality{*amounts} >= quality); + } } else if (status == Succeed) { if (!features[fixAMMv1_1]) + { BEAST_EXPECT( Quality{*amounts} >= quality || withinRelativeDistance(Quality{*amounts}, quality, Number{1, -7})); + } else + { BEAST_EXPECT(Quality{*amounts} >= quality); + } } else if (status == FailShouldSucceed) { @@ -5741,7 +5816,7 @@ private: takerPays, swapAssetIn(Amounts{poolIn, poolOut}, takerPays, tfee)}; } - else if (isXRP(poolOut)) + if (isXRP(poolOut)) { auto const takerGets = STAmount{xrpIssue(), 1}; return Amounts{ @@ -5786,7 +5861,7 @@ private: using namespace jtx; testAMM([&](AMM& ammAlice, Env& env) { - WithdrawArg args{ + WithdrawArg const args{ .flags = tfSingleAsset, .err = ter(temMALFORMED), }; @@ -5794,7 +5869,7 @@ private: }); testAMM([&](AMM& ammAlice, Env& env) { - WithdrawArg args{ + WithdrawArg const args{ .flags = tfOneAssetLPToken, .err = ter(temMALFORMED), }; @@ -5802,7 +5877,7 @@ private: }); testAMM([&](AMM& ammAlice, Env& env) { - WithdrawArg args{ + WithdrawArg const args{ .flags = tfLimitLPToken, .err = ter(temMALFORMED), }; @@ -5810,7 +5885,7 @@ private: }); testAMM([&](AMM& ammAlice, Env& env) { - WithdrawArg args{ + WithdrawArg const args{ .asset1Out = XRP(100), .asset2Out = XRP(100), .err = ter(temBAD_AMM_TOKENS), @@ -5819,7 +5894,7 @@ private: }); testAMM([&](AMM& ammAlice, Env& env) { - WithdrawArg args{ + WithdrawArg const args{ .asset1Out = XRP(100), .asset2Out = BAD(100), .err = ter(temBAD_CURRENCY), @@ -5871,7 +5946,7 @@ private: STAmount const goodUsdBIT; STAmount const goodUsdBITr; IOUAmount const lpTokenBalance; - std::optional const lpTokenBalanceAlt = {}; + std::optional const lpTokenBalanceAlt = std::nullopt; double const offer1BtcGH = 0.1; double const offer2BtcGH = 0.1; double const offer2UsdGH = 1; @@ -6097,7 +6172,7 @@ private: env(pay(bitstamp, trader, usdBIT(100'000))); env.close(); - AMM amm{env, trader, usdGH(input.poolUsdGH), usdBIT(input.poolUsdBIT)}; + AMM const amm{env, trader, usdGH(input.poolUsdGH), usdBIT(input.poolUsdBIT)}; env.close(); IOUAmount const preSwapLPTokenBalance = amm.getLPTokensBalance(); @@ -6117,9 +6192,13 @@ private: auto const failUsdBIT = features[fixAMMv1_1] ? input.failUsdBITr : input.failUsdBIT; auto const goodUsdGH = features[fixAMMv1_1] ? input.goodUsdGHr : input.goodUsdGH; auto const goodUsdBIT = features[fixAMMv1_1] ? input.goodUsdBITr : input.goodUsdBIT; - auto const lpTokenBalance = env.enabled(fixAMMv1_3) && input.lpTokenBalanceAlt - ? *input.lpTokenBalanceAlt - : input.lpTokenBalance; + auto const lpTokenBalance = [&] { + if (not env.enabled(fixAMMv1_3)) + return input.lpTokenBalance; + + return input.lpTokenBalanceAlt.value_or(input.lpTokenBalance); + }(); + if (!features[fixAMMOverflowOffer]) { BEAST_EXPECT(amm.expectBalances(failUsdGH, failUsdBIT, lpTokenBalance)); @@ -6202,7 +6281,7 @@ private: env(offer(alice, XRP(1), USD(0.01))); env.close(); - AMM amm(env, gw, XRP(200'000), USD(100'000)); + AMM const amm(env, gw, XRP(200'000), USD(100'000)); // The offer doesn't cross AMM in pre-amendment code // It crosses AMM in post-amendment code @@ -6235,7 +6314,7 @@ private: // There is no blocking offer // env(offer(alice, XRP(1), USD(0.01))); - AMM amm(env, gw, XRP(200'000), USD(100'000)); + AMM const amm(env, gw, XRP(200'000), USD(100'000)); // The offer crosses AMM env(offer(carol, USD(0.49), XRP(1))); @@ -6257,7 +6336,7 @@ private: // It crosses AMM in post-amendment code env(offer(bob, USD(1), XRPAmount(500))); env.close(); - AMM amm(env, alice, XRP(1'000), USD(500)); + AMM const amm(env, alice, XRP(1'000), USD(500)); env(offer(carol, XRP(100), USD(55))); env.close(); if (!features[fixAMMv1_1]) @@ -6287,7 +6366,7 @@ private: Env env(*this, features); fund(env, gw, {alice, carol, bob}, XRP(10'000), {USD(1'000)}); - AMM amm(env, alice, XRP(1'000), USD(500)); + AMM const amm(env, alice, XRP(1'000), USD(500)); env(offer(carol, XRP(100), USD(55))); env.close(); BEAST_EXPECT(amm.expectBalances( @@ -6420,12 +6499,12 @@ private: // clawback-enabled issuer if (!features[featureAMMClawback]) { - AMM amm(env, gw, XRP(100), USD(100), ter(tecNO_PERMISSION)); - AMM amm1(env, alice, USD(100), XRP(100), ter(tecNO_PERMISSION)); + AMM const amm(env, gw, XRP(100), USD(100), ter(tecNO_PERMISSION)); + AMM const amm1(env, alice, USD(100), XRP(100), ter(tecNO_PERMISSION)); env(fclear(gw, asfAllowTrustLineClawback)); env.close(); // Can't be cleared - AMM amm2(env, gw, XRP(100), USD(100), ter(tecNO_PERMISSION)); + AMM const amm2(env, gw, XRP(100), USD(100), ter(tecNO_PERMISSION)); } // If featureAMMClawback is enabled, AMMCreate is allowed for // clawback-enabled issuer. Clawback from the AMM Account is not @@ -6434,8 +6513,8 @@ private: // AMMClawback transaction to claw back from AMM Account. else { - AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); - AMM amm1(env, alice, USD(100), XRP(200), ter(tecDUPLICATE)); + AMM const amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); + AMM const amm1(env, alice, USD(100), XRP(200), ter(tecDUPLICATE)); // Construct the amount being clawed back using AMM account. // By doing this, we make the clawback transaction's Amount field's @@ -6447,7 +6526,7 @@ private: // is confusing. auto const error = features[featureSingleAssetVault] ? ter{tecPSEUDO_ACCOUNT} : ter{tecAMM_ACCOUNT}; - Issue usd(USD.issue().currency, amm.ammAccount()); + Issue const usd(USD.issue().currency, amm.ammAccount()); auto amount = amountFromString(usd, "10"); env(claw(gw, amount), error); } @@ -6571,8 +6650,8 @@ private: env(pay(gw, alice, USD(10'000))); env.close(); - STAmount amount = XRP(10'000); - STAmount amount2 = USD(10'000); + STAmount const amount = XRP(10'000); + STAmount const amount2 = USD(10'000); auto const keylet = keylet::amm(amount.issue(), amount2.issue()); for (int i = 0; i < 256; ++i) { @@ -6584,7 +6663,7 @@ private: sig(autofill)); } - AMM ammAlice( + AMM const ammAlice( env, alice, amount, @@ -6606,7 +6685,7 @@ private: STAmount xrpBalance{XRPAmount(692'614'492'126)}; STAmount xpmBalance{XPM, UINT64_C(18'610'359'80246901), -8}; STAmount amount{XPM, UINT64_C(6'566'496939465400), -12}; - std::uint16_t tfee = 941; + std::uint16_t const tfee = 941; auto test = [&](auto&& cb, std::uint16_t tfee_) { Env env(*this, features); @@ -6617,7 +6696,7 @@ private: AMM amm(env, gw, xrpBalance, xpmBalance, CreateArg{.tfee = tfee_}); // AMM LPToken balance required to replicate single deposit failure - STAmount lptAMMBalance{amm.lptIssue(), UINT64_C(3'234'987'266'485968), -6}; + STAmount const lptAMMBalance{amm.lptIssue(), UINT64_C(3'234'987'266'485968), -6}; auto const burn = IOUAmount{amm.getLPTokensBalance() - lptAMMBalance}; // burn tokens to get to the required AMM state env(amm.bid(BidArg{.account = gw, .bidMin = burn, .bidMax = burn})); @@ -6636,9 +6715,13 @@ private: amm.withdraw(WithdrawArg{.asset1Out = STAmount{XPM, 1, -5}}); auto const [amount_, amount2_, lptAMM_] = amm.balances(XRP, XPM); if (!env.enabled(fixAMMv1_3)) + { BEAST_EXPECT((amount2 - amount2_) > withdraw); + } else + { BEAST_EXPECT((amount2 - amount2_) <= withdraw); + } }, 0); } @@ -6648,14 +6731,18 @@ private: { auto const [amount, amount2, lptBalance] = amm.balances(GBP, EUR); - NumberMantissaScaleGuard sg(MantissaRange::small); - NumberRoundModeGuard g(env.enabled(fixAMMv1_3) ? Number::upward : Number::getround()); + NumberMantissaScaleGuard const sg(MantissaRange::small); + NumberRoundModeGuard const g(env.enabled(fixAMMv1_3) ? Number::upward : Number::getround()); auto const res = root2(amount * amount2); if (shouldFail) + { BEAST_EXPECT(res < lptBalance); + } else + { BEAST_EXPECT(res >= lptBalance); + } } void diff --git a/src/test/app/AccountDelete_test.cpp b/src/test/app/AccountDelete_test.cpp index 91780800e9..4382fb27c7 100644 --- a/src/test/app/AccountDelete_test.cpp +++ b/src/test/app/AccountDelete_test.cpp @@ -452,7 +452,7 @@ public: // Verify the existence of the expected ledger entries. Keylet const aliceOwnerDirKey{keylet::ownerDir(alice.id())}; { - std::shared_ptr closed{env.closed()}; + std::shared_ptr const closed{env.closed()}; BEAST_EXPECT(closed->exists(keylet::account(alice.id()))); BEAST_EXPECT(closed->exists(aliceOwnerDirKey)); @@ -486,7 +486,7 @@ public: // Verify that alice's account root is gone as well as her directory // nodes and all of her offers. { - std::shared_ptr closed{env.closed()}; + std::shared_ptr const closed{env.closed()}; BEAST_EXPECT(!closed->exists(keylet::account(alice.id()))); BEAST_EXPECT(!closed->exists(aliceOwnerDirKey)); @@ -539,7 +539,7 @@ public: env(acctdelete(gw, alice), fee(acctDelFee), ter(tecHAS_OBLIGATIONS)); env.close(); { - std::shared_ptr closed{env.closed()}; + std::shared_ptr const closed{env.closed()}; BEAST_EXPECT(closed->exists(keylet::account(alice.id()))); BEAST_EXPECT(closed->exists(keylet::account(gw.id()))); } @@ -590,7 +590,7 @@ public: env(acctdelete(alice, env.master), fee(XRP(1)), ter(telINSUF_FEE_P)); env.close(); { - std::shared_ptr closed{env.closed()}; + std::shared_ptr const closed{env.closed()}; BEAST_EXPECT(closed->exists(keylet::account(alice.id()))); BEAST_EXPECT(env.balance(env.master) == masterBalance); } @@ -617,7 +617,7 @@ public: env.require(owners(bob, 250)); { - std::shared_ptr closed{env.closed()}; + std::shared_ptr const closed{env.closed()}; BEAST_EXPECT(closed->exists(keylet::account(bob.id()))); for (std::uint32_t i = 0; i < 250; ++i) { @@ -636,7 +636,7 @@ public: verifyDeliveredAmount(env, bobOldBalance - acctDelFee); env.close(); { - std::shared_ptr closed{env.closed()}; + std::shared_ptr const closed{env.closed()}; BEAST_EXPECT(!closed->exists(keylet::account(bob.id()))); for (std::uint32_t i = 0; i < 250; ++i) { diff --git a/src/test/app/AccountSet_test.cpp b/src/test/app/AccountSet_test.cpp index 87c6474be2..246f18c445 100644 --- a/src/test/app/AccountSet_test.cpp +++ b/src/test/app/AccountSet_test.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -189,12 +190,12 @@ public: BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfDomain)); // The upper limit on the length is 256 bytes - // (defined as DOMAIN_BYTES_MAX in SetAccount) + // (defined as DOMAIN_BYTES_MAX in AccountSet) // test the edge cases: 255, 256, 257. std::size_t const maxLength = 256; for (std::size_t len = maxLength - 1; len <= maxLength + 1; ++len) { - std::string domain2 = std::string(len - domain.length() - 1, 'a') + "." + domain; + std::string const domain2 = std::string(len - domain.length() - 1, 'a') + "." + domain; BEAST_EXPECT(domain2.length() == len); @@ -307,9 +308,13 @@ public: // If the field is not present expect the default value if (!(*env.le(alice))[~sfTransferRate]) + { BEAST_EXPECT(r.get == 1.0); + } else + { BEAST_EXPECT(*(*env.le(alice))[~sfTransferRate] == r.get * QUALITY_ONE); + } } }; @@ -368,7 +373,7 @@ public: // // Two out-of-bound values are currently in the ledger (March 2020) // They are 4.0 and 4.294967295. So those are the values we test. - for (double transferRate : {4.0, 4.294967295}) + for (double const transferRate : {4.0, 4.294967295}) { Env env(*this); env.fund(XRP(10000), gw, alice, bob); @@ -398,7 +403,7 @@ public: // Note that we're bypassing almost all of the ledger's safety // checks with this modify() call. If you call close() between // here and the end of the test all the effort will be lost. - env.app().openLedger().modify([&gw, transferRate](OpenView& view, beast::Journal j) { + env.app().getOpenLedger().modify([&gw, transferRate](OpenView& view, beast::Journal j) { // Get the account root we want to hijack. auto const sle = view.read(keylet::account(gw.id())); if (!sle) @@ -547,7 +552,7 @@ public: auto stx = std::make_shared(*jtx.stx); stx->at(sfSigningPubKey) = makeSlice(std::string("badkey")); - env.app().openLedger().modify([&](OpenView& view, beast::Journal j) { + env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) { auto const result = xrpl::apply(env.app(), view, *stx, tapNONE, j); BEAST_EXPECT(result.ter == temBAD_SIGNATURE); BEAST_EXPECT(!result.applied); diff --git a/src/test/app/AccountTxPaging_test.cpp b/src/test/app/AccountTxPaging_test.cpp index 5efe307812..1f2e909927 100644 --- a/src/test/app/AccountTxPaging_test.cpp +++ b/src/test/app/AccountTxPaging_test.cpp @@ -10,7 +10,7 @@ namespace xrpl { class AccountTxPaging_test : public beast::unit_test::suite { - bool + static bool checkTransaction(Json::Value const& tx, int sequence, int ledger) { return ( @@ -18,7 +18,7 @@ class AccountTxPaging_test : public beast::unit_test::suite tx[jss::tx][jss::ledger_index].asInt() == ledger); } - auto + static auto next( test::jtx::Env& env, test::jtx::Account const& account, @@ -47,9 +47,9 @@ class AccountTxPaging_test : public beast::unit_test::suite using namespace test::jtx; Env env(*this); - Account A1{"A1"}; - Account A2{"A2"}; - Account A3{"A3"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const A3{"A3"}; env.fund(XRP(10000), A1, A2, A3); env.close(); diff --git a/src/test/app/AmendmentTable_test.cpp b/src/test/app/AmendmentTable_test.cpp index 3b14c8b622..007715f9d1 100644 --- a/src/test/app/AmendmentTable_test.cpp +++ b/src/test/app/AmendmentTable_test.cpp @@ -390,7 +390,7 @@ public: { uint256 const supportedID = amendmentId(a); bool const enabled = table->isEnabled(supportedID); - bool const found = allEnabled.find(supportedID) != allEnabled.end(); + bool const found = allEnabled.contains(supportedID); BEAST_EXPECTS( enabled == found, a + (enabled ? " enabled " : " disabled ") + (found ? " found" : " not found")); @@ -404,7 +404,7 @@ public: std::vector const desired = table->getDesired(); for (uint256 const& a : desired) - BEAST_EXPECT(vetoed.count(a) == 0); + BEAST_EXPECT(not vetoed.contains(a)); // Unveto an amendment that is already not vetoed. Shouldn't // hurt anything, but the values returned by getDesired() @@ -439,7 +439,7 @@ public: // Make a list of trusted validators. // Register the validators with AmendmentTable and return the list. - std::vector> + static std::vector> makeValidators(int num, std::unique_ptr const& table) { std::vector> ret; @@ -462,7 +462,7 @@ public: } // Execute a pretend consensus round for a flag ledger - void + static void doRound( Rules const& rules, AmendmentTable& table, @@ -526,22 +526,22 @@ public: { case 0: // amendment goes from majority to enabled - if (enabled.find(hash) != enabled.end()) + if (enabled.contains(hash)) Throw("enabling already enabled"); - if (majority.find(hash) == majority.end()) + if (!majority.contains(hash)) Throw("enabling without majority"); enabled.insert(hash); majority.erase(hash); break; case tfGotMajority: - if (majority.find(hash) != majority.end()) + if (majority.contains(hash)) Throw("got majority while having majority"); majority[hash] = roundTime; break; case tfLostMajority: - if (majority.find(hash) == majority.end()) + if (!majority.contains(hash)) Throw("lost majority without majority"); majority.erase(hash); break; @@ -719,7 +719,7 @@ public: BEAST_EXPECT(ourVotes.size() == yes_.size()); BEAST_EXPECT(enabled.empty()); for (auto const& i : yes_) - BEAST_EXPECT(majority.find(amendmentId(i)) == majority.end()); + BEAST_EXPECT(not majority.contains(amendmentId(i))); // Now, everyone votes for this feature for (auto const& i : yes_) @@ -766,7 +766,7 @@ public: BEAST_EXPECT(enabled.size() == yes_.size()); BEAST_EXPECT(ourVotes.empty()); for (auto const& i : yes_) - BEAST_EXPECT(majority.find(amendmentId(i)) == majority.end()); + BEAST_EXPECT(not majority.contains(amendmentId(i))); } // Detect majority at 80%, enable later @@ -1067,7 +1067,7 @@ public: // Since the local validator vote record expires after 24 hours, // with 23 hour flapping the amendment will go live. But with 25 // hour flapping the amendment will not go live. - for (int flapRateHours : {23, 25}) + for (int const flapRateHours : {23, 25}) { test::jtx::Env env{*this, feat}; auto const testAmendment = amendmentId("validatorFlapping"); diff --git a/src/test/app/Batch_test.cpp b/src/test/app/Batch_test.cpp index 8fcadcb1a5..f301b6d60f 100644 --- a/src/test/app/Batch_test.cpp +++ b/src/test/app/Batch_test.cpp @@ -35,7 +35,7 @@ class Batch_test : public beast::unit_test::suite std::string txHash; }; - Json::Value + static Json::Value getTxByIndex(Json::Value const& jrr, int const index) { for (auto const& txn : jrr[jss::result][jss::ledger][jss::transactions]) @@ -46,7 +46,7 @@ class Batch_test : public beast::unit_test::suite return {}; } - Json::Value + static Json::Value getLastLedger(jtx::Env& env) { Json::Value params; @@ -93,6 +93,7 @@ class Batch_test : public beast::unit_test::suite auto const ids = batchTxn.stx->getBatchTransactionIDs(); std::vector txIDs; + txIDs.reserve(ids.size()); for (auto const& id : ids) txIDs.push_back(strHex(id)); TxID const batchID = batchTxn.stx->getTransactionID(); @@ -125,7 +126,7 @@ class Batch_test : public beast::unit_test::suite return p; } - auto + static auto openLedgerFee(jtx::Env& env, XRPAmount const& batchFee) { using namespace jtx; @@ -959,7 +960,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, }; validateClosedLedger(env, testCases); @@ -968,7 +969,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { // next ledger is empty - std::vector testCases = {}; + std::vector const testCases = {}; validateClosedLedger(env, testCases); } @@ -1001,7 +1002,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, }; validateClosedLedger(env, testCases); @@ -1010,7 +1011,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { // next ledger is empty - std::vector testCases = {}; + std::vector const testCases = {}; validateClosedLedger(env, testCases); } @@ -1043,7 +1044,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, }; validateClosedLedger(env, testCases); @@ -1052,7 +1053,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { // next ledger is empty - std::vector testCases = {}; + std::vector const testCases = {}; validateClosedLedger(env, testCases); } @@ -1085,7 +1086,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, }; validateClosedLedger(env, testCases); @@ -1094,7 +1095,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { // next ledger is empty - std::vector testCases = {}; + std::vector const testCases = {}; validateClosedLedger(env, testCases); } @@ -1127,7 +1128,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, }; validateClosedLedger(env, testCases); @@ -1136,7 +1137,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { // next ledger is empty - std::vector testCases = {}; + std::vector const testCases = {}; validateClosedLedger(env, testCases); } @@ -1379,7 +1380,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(1)), aliceSeq), batch::inner(pay(alice, bob, XRP(1)), aliceSeq)); - env.app().openLedger().modify([&](OpenView& view, beast::Journal j) { + env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) { auto const result = xrpl::apply(env.app(), view, *jt.stx, tapNONE, j); BEAST_EXPECT(!result.applied && result.ter == temARRAY_TOO_LARGE); return result.applied; @@ -1422,7 +1423,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(5)), aliceSeq + 2), batch::sig(bob, bob, bob, bob, bob, bob, bob, bob, bob, bob)); - env.app().openLedger().modify([&](OpenView& view, beast::Journal j) { + env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) { auto const result = xrpl::apply(env.app(), view, *jt.stx, tapNONE, j); BEAST_EXPECT(!result.applied && result.ter == temARRAY_TOO_LARGE); return result.applied; @@ -1462,7 +1463,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(2)), seq + 2)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -1494,7 +1495,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(9999)), seq + 2)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, }; validateClosedLedger(env, testCases); @@ -1523,7 +1524,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(trust(alice, USD(1000), tfSetfAuth), seq + 2)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, }; validateClosedLedger(env, testCases); @@ -1552,7 +1553,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(trust(alice, USD(1000), tfSetfAuth), 0, seq + 2)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, }; validateClosedLedger(env, testCases); @@ -1604,7 +1605,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(9999)), seq + 3)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tecUNFUNDED_PAYMENT", txIDs[0], batchID}, {2, "Payment", "tecUNFUNDED_PAYMENT", txIDs[1], batchID}, @@ -1637,7 +1638,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(2)), seq + 3)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tecUNFUNDED_PAYMENT", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -1669,7 +1670,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(2)), seq + 3)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, }; @@ -1700,7 +1701,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(2)), seq + 3)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[1], batchID}, }; @@ -1731,7 +1732,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(2)), seq + 3)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[1], batchID}, }; @@ -1768,7 +1769,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, dave, XRP(100)), seq + 6)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "OfferCreate", "tecKILLED", txIDs[0], batchID}, {2, "OfferCreate", "tecKILLED", txIDs[1], batchID}, @@ -1820,7 +1821,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(3)), seq + 4)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tecUNFUNDED_PAYMENT", txIDs[0], batchID}, }; @@ -1851,7 +1852,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(4)), seq + 4)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -1886,7 +1887,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(3)), seq + 4)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -1920,7 +1921,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(3)), seq + 4)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -1953,7 +1954,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(3)), seq + 4)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -1986,7 +1987,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, dave, XRP(100)), seq + 4)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -2037,7 +2038,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(3)), seq + 4)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tecUNFUNDED_PAYMENT", txIDs[1], batchID}, @@ -2072,7 +2073,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(3)), seq + 4)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -2107,7 +2108,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(3)), seq + 3)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -2141,7 +2142,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(3)), seq + 3)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -2174,7 +2175,7 @@ class Batch_test : public beast::unit_test::suite offer(alice, alice["USD"](100), XRP(100), tfImmediateOrCancel), seq + 3)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -2259,7 +2260,7 @@ class Batch_test : public beast::unit_test::suite txn[sfTxnSignature] = "DEADBEEF"; STParsedJSONObject parsed("test", txn.getTxn()); Serializer s; - parsed.object->add(s); + parsed.object->add(s); // NOLINT(bugprone-unchecked-optional-access) submitAndValidate("TxnSignature set", s.slice(), __LINE__); } @@ -2273,7 +2274,7 @@ class Batch_test : public beast::unit_test::suite txn[sfSigningPubKey] = strHex(alice.pk()); STParsedJSONObject parsed("test", txn.getTxn()); Serializer s; - parsed.object->add(s); + parsed.object->add(s); // NOLINT(bugprone-unchecked-optional-access) submitAndValidate( "SigningPubKey set", s.slice(), @@ -2292,7 +2293,7 @@ class Batch_test : public beast::unit_test::suite txn[sfSigners] = Json::arrayValue; STParsedJSONObject parsed("test", txn.getTxn()); Serializer s; - parsed.object->add(s); + parsed.object->add(s); // NOLINT(bugprone-unchecked-optional-access) submitAndValidate( "Signers set", s.slice(), @@ -2308,7 +2309,7 @@ class Batch_test : public beast::unit_test::suite STParsedJSONObject parsed("test", jt.jv); Serializer s; - parsed.object->add(s); + parsed.object->add(s); // NOLINT(bugprone-unchecked-optional-access) submitAndValidate( "Fully signed", s.slice(), __LINE__, std::nullopt, std::nullopt, !withBatch); } @@ -2322,7 +2323,7 @@ class Batch_test : public beast::unit_test::suite auto txn = batch::inner(pay(alice, bob, XRP(1)), env.seq(alice)); STParsedJSONObject parsed("test", txn.getTxn()); Serializer s; - parsed.object->add(s); + parsed.object->add(s); // NOLINT(bugprone-unchecked-optional-access) submitAndValidate( "No signing fields set", s.slice(), @@ -2338,7 +2339,7 @@ class Batch_test : public beast::unit_test::suite // - has no `Signers` field // + has `tfInnerBatchTxn` flag { - STTx amendTx(ttAMENDMENT, [seq = env.closed()->header().seq + 1](auto& obj) { + STTx const amendTx(ttAMENDMENT, [seq = env.closed()->header().seq + 1](auto& obj) { obj.setAccountID(sfAccount, AccountID()); obj.setFieldH256(sfAmendment, fixBatchInnerSigs); obj.setFieldU32(sfLedgerSequence, seq); @@ -2347,7 +2348,7 @@ class Batch_test : public beast::unit_test::suite auto txn = batch::inner(amendTx.getJson(JsonOptions::none), env.seq(alice)); STParsedJSONObject parsed("test", txn.getTxn()); Serializer s; - parsed.object->add(s); + parsed.object->add(s); // NOLINT(bugprone-unchecked-optional-access) submitAndValidate( "Pseudo-transaction", s.slice(), @@ -2396,7 +2397,7 @@ class Batch_test : public beast::unit_test::suite batch::sig(bob)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "AccountSet", "tesSUCCESS", txIDs[1], batchID}, @@ -2445,7 +2446,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(1)), seq + 2)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "AccountSet", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -2500,7 +2501,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(2)), seq + 3)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "AccountDelete", "tesSUCCESS", txIDs[1], batchID}, @@ -2543,7 +2544,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(2)), seq + 3)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "AccountDelete", "tecHAS_OBLIGATIONS", txIDs[1], batchID}, @@ -2584,7 +2585,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(2)), seq + 3)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, }; validateClosedLedger(env, testCases); @@ -2625,7 +2626,7 @@ class Batch_test : public beast::unit_test::suite // Just use an XRP asset PrettyAsset const asset{xrpIssue(), 1'000'000}; - Vault vault{env}; + Vault const vault{env}; auto const deposit = asset(50'000); auto const debtMaximumValue = asset(25'000).value(); @@ -2820,7 +2821,7 @@ class Batch_test : public beast::unit_test::suite batch::sig(bob)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "CheckCreate", "tesSUCCESS", txIDs[0], batchID}, {2, "CheckCash", "tesSUCCESS", txIDs[1], batchID}, @@ -2866,7 +2867,7 @@ class Batch_test : public beast::unit_test::suite batch::sig(bob)); env.close(); - std::vector testCases = { + 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}, @@ -2931,7 +2932,7 @@ class Batch_test : public beast::unit_test::suite batch::sig(bob)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "TicketCreate", "tesSUCCESS", txIDs[0], batchID}, {2, "CheckCreate", "tesSUCCESS", txIDs[1], batchID}, @@ -2991,7 +2992,7 @@ class Batch_test : public beast::unit_test::suite batch::sig(alice, bob)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "CheckCreate", "tesSUCCESS", txIDs[0], batchID}, {2, "CheckCash", "tesSUCCESS", txIDs[1], batchID}, @@ -3025,7 +3026,7 @@ class Batch_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob); env.close(); - std::uint32_t aliceTicketSeq{env.seq(alice) + 1}; + std::uint32_t const aliceTicketSeq{env.seq(alice) + 1}; env(ticket::create(alice, 10)); env.close(); @@ -3043,7 +3044,7 @@ class Batch_test : public beast::unit_test::suite ticket::use(aliceTicketSeq)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -3091,7 +3092,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(2)), 0, aliceTicketSeq + 1)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -3122,7 +3123,7 @@ class Batch_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob); env.close(); - std::uint32_t aliceTicketSeq{env.seq(alice) + 1}; + std::uint32_t const aliceTicketSeq{env.seq(alice) + 1}; env(ticket::create(alice, 10)); env.close(); @@ -3140,7 +3141,7 @@ class Batch_test : public beast::unit_test::suite ticket::use(aliceTicketSeq)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -3201,7 +3202,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -3212,7 +3213,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { // next ledger contains noop txn - std::vector testCases = { + std::vector const testCases = { {0, "AccountSet", "tesSUCCESS", noopTxnID, std::nullopt}, }; validateClosedLedger(env, testCases); @@ -3245,7 +3246,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -3256,7 +3257,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { // next ledger is empty - std::vector testCases = {}; + std::vector const testCases = {}; validateClosedLedger(env, testCases); } } @@ -3284,7 +3285,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -3295,7 +3296,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { // next ledger is empty - std::vector testCases = {}; + std::vector const testCases = {}; validateClosedLedger(env, testCases); } } @@ -3326,7 +3327,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { - std::vector testCases = { + std::vector const testCases = { {0, "AccountSet", "tesSUCCESS", noopTxnID, std::nullopt}, {1, "Batch", "tesSUCCESS", batchID, std::nullopt}, {2, "Payment", "tesSUCCESS", txIDs[0], batchID}, @@ -3338,7 +3339,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { // next ledger contains no transactions - std::vector testCases = {}; + std::vector const testCases = {}; validateClosedLedger(env, testCases); } } @@ -3364,7 +3365,7 @@ class Batch_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob); env.close(); - std::uint32_t aliceTicketSeq{env.seq(alice) + 1}; + std::uint32_t const aliceTicketSeq{env.seq(alice) + 1}; env(ticket::create(alice, 10)); env.close(); @@ -3386,7 +3387,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -3397,7 +3398,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { // next ledger is empty - std::vector testCases = {}; + std::vector const testCases = {}; validateClosedLedger(env, testCases); } } @@ -3411,7 +3412,7 @@ class Batch_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob); env.close(); - std::uint32_t aliceTicketSeq{env.seq(alice) + 1}; + std::uint32_t const aliceTicketSeq{env.seq(alice) + 1}; env(ticket::create(alice, 10)); env.close(); @@ -3433,7 +3434,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -3444,7 +3445,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { // next ledger is empty - std::vector testCases = {}; + std::vector const testCases = {}; validateClosedLedger(env, testCases); } } @@ -3472,7 +3473,7 @@ class Batch_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob); env.close(); - std::uint32_t aliceTicketSeq{env.seq(alice) + 1}; + std::uint32_t const aliceTicketSeq{env.seq(alice) + 1}; env(ticket::create(alice, 10)); env.close(); @@ -3496,7 +3497,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "CheckCreate", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -3508,7 +3509,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { // next ledger is empty - std::vector testCases = {}; + std::vector const testCases = {}; validateClosedLedger(env, testCases); } } @@ -3519,7 +3520,7 @@ class Batch_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob); env.close(); - std::uint32_t aliceTicketSeq{env.seq(alice) + 1}; + std::uint32_t const aliceTicketSeq{env.seq(alice) + 1}; env(ticket::create(alice, 10)); env.close(); @@ -3545,7 +3546,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { - std::vector testCases = { + std::vector const testCases = { {0, "CheckCreate", "tesSUCCESS", objTxnID, std::nullopt}, {1, "Batch", "tesSUCCESS", batchID, std::nullopt}, {2, "CheckCash", "tesSUCCESS", txIDs[0], batchID}, @@ -3566,7 +3567,7 @@ class Batch_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob); env.close(); - std::uint32_t aliceTicketSeq{env.seq(alice) + 1}; + std::uint32_t const aliceTicketSeq{env.seq(alice) + 1}; env(ticket::create(alice, 10)); env.close(); @@ -3590,7 +3591,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "CheckCreate", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -3627,7 +3628,7 @@ class Batch_test : public beast::unit_test::suite BEAST_EXPECT(isPseudoTx(stx)); BEAST_EXPECT(!passesLocalChecks(stx, reason)); BEAST_EXPECT(reason == "Cannot submit pseudo transactions."); - env.app().openLedger().modify([&](OpenView& view, beast::Journal j) { + env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) { auto const result = xrpl::apply(env.app(), view, stx, tapNONE, j); BEAST_EXPECT(!result.applied && result.ter == temINVALID_FLAG); return result.applied; @@ -3686,7 +3687,7 @@ class Batch_test : public beast::unit_test::suite env(payTxn2, ter(terPRE_SEQ)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Payment", "tesSUCCESS", payTxn1ID, std::nullopt}, {1, "Batch", "tesSUCCESS", batchID, std::nullopt}, {2, "Payment", "tesSUCCESS", txIDs[0], batchID}, @@ -3697,7 +3698,7 @@ class Batch_test : public beast::unit_test::suite env.close(); { // next ledger includes the payment txn - std::vector testCases = { + std::vector const testCases = { {0, "Payment", "tesSUCCESS", payTxn2ID, std::nullopt}, }; validateClosedLedger(env, testCases); @@ -3909,7 +3910,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(2)), seq + 2)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -3958,7 +3959,7 @@ class Batch_test : public beast::unit_test::suite batch::sig(bob)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -4008,7 +4009,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(2)), seq + 2)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "AccountSet", "tesSUCCESS", txIDs[0], batchID}, {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, @@ -4028,8 +4029,8 @@ class Batch_test : public beast::unit_test::suite // MPTokenIssuanceSet with granular permission { test::jtx::Env env{*this, features}; - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(100000), alice, bob); env.close(); @@ -4070,7 +4071,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(jv2, seq + 2)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "MPTokenIssuanceSet", "tesSUCCESS", txIDs[0], batchID}, {2, "MPTokenIssuanceSet", "tesSUCCESS", txIDs[1], batchID}, @@ -4083,9 +4084,9 @@ class Batch_test : public beast::unit_test::suite // with granular permission { test::jtx::Env env{*this, features}; - Account gw{"gw"}; - Account alice{"alice"}; - Account bob{"bob"}; + Account const gw{"gw"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(10000), gw, alice, bob); env(fset(gw, asfRequireAuth)); env.close(); @@ -4111,7 +4112,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(jv2, seq + 2)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "TrustSet", "tesSUCCESS", txIDs[0], batchID}, {2, "TrustSet", "tesSUCCESS", txIDs[1], batchID}, @@ -4122,9 +4123,9 @@ class Batch_test : public beast::unit_test::suite // inner transaction not authorized by the delegating account. { test::jtx::Env env{*this, features}; - Account gw{"gw"}; - Account alice{"alice"}; - Account bob{"bob"}; + Account const gw{"gw"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(10000), gw, alice, bob); env(fset(gw, asfRequireAuth)); env.close(); @@ -4151,7 +4152,7 @@ class Batch_test : public beast::unit_test::suite batch::inner(jv2, seq + 2)); env.close(); - std::vector testCases = { + std::vector const testCases = { {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, {1, "TrustSet", "tesSUCCESS", txIDs[0], batchID}, }; diff --git a/src/test/app/Check_test.cpp b/src/test/app/Check_test.cpp index 05c0870329..1e5d90f650 100644 --- a/src/test/app/Check_test.cpp +++ b/src/test/app/Check_test.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -272,7 +273,7 @@ class Check_test : public beast::unit_test::suite env(check::create(from, to, USD(50)), ter(expected)); env.close(); - if (expected == tesSUCCESS) + if (isTesSuccess(expected)) { BEAST_EXPECT(checksOnAccount(env, from).size() == fromCkCount + 2); BEAST_EXPECT(checksOnAccount(env, to).size() == toCkCount + 2); @@ -528,8 +529,8 @@ class Check_test : public beast::unit_test::suite env.close(); env.require(balance(alice, startBalance - XRP(10) - drops(baseFeeDrops))); env.require(balance(bob, startBalance + XRP(10) - drops(baseFeeDrops))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(ownerCount(env, bob) == 0); @@ -562,8 +563,8 @@ class Check_test : public beast::unit_test::suite verifyDeliveredAmount(env, drops(checkAmount.mantissa())); env.require(balance(alice, reserve)); env.require(balance(bob, startBalance + checkAmount - drops(baseFeeDrops * 3))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(ownerCount(env, bob) == 0); @@ -592,8 +593,8 @@ class Check_test : public beast::unit_test::suite verifyDeliveredAmount(env, drops(checkAmount.mantissa() - 1)); env.require(balance(alice, reserve)); env.require(balance(bob, startBalance + checkAmount - drops(baseFeeDrops * 2 + 1))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(ownerCount(env, bob) == 0); @@ -663,8 +664,8 @@ class Check_test : public beast::unit_test::suite env.close(); env.require(balance(alice, USD(0))); env.require(balance(bob, USD(10))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(ownerCount(env, bob) == 1); @@ -688,8 +689,8 @@ class Check_test : public beast::unit_test::suite env.close(); env.require(balance(alice, USD(2))); env.require(balance(bob, USD(8))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(ownerCount(env, bob) == 1); @@ -755,8 +756,8 @@ class Check_test : public beast::unit_test::suite env.close(); env.require(balance(alice, USD(0))); env.require(balance(bob, USD(10))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(ownerCount(env, bob) == 1); } @@ -838,8 +839,8 @@ class Check_test : public beast::unit_test::suite verifyDeliveredAmount(env, USD(2)); env.require(balance(alice, USD(0))); env.require(balance(bob, USD(8))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(ownerCount(env, bob) == 1); } @@ -890,8 +891,8 @@ class Check_test : public beast::unit_test::suite env.require(balance(alice, USD(8) - bobGot)); env.require(balance(bob, bobGot)); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(ownerCount(env, bob) == 1); } @@ -945,8 +946,8 @@ class Check_test : public beast::unit_test::suite env.close(); env.require(balance(alice, USD(5))); env.require(balance(bob, USD(3))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(ownerCount(env, bob) == 2); } @@ -1018,8 +1019,8 @@ class Check_test : public beast::unit_test::suite env.close(); env.require(balance(alice, USD(1000 - 125 - 60))); env.require(balance(bob, USD(0 + 100 + 50))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); } void @@ -1655,7 +1656,7 @@ class Check_test : public beast::unit_test::suite env(check::cancel(bob, chkIdNotExp3)); env.close(); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); BEAST_EXPECT(ownerCount(env, alice) == 1); } } @@ -1815,7 +1816,7 @@ class Check_test : public beast::unit_test::suite env.require(owners(alice, 6)); env.require(tickets(alice, env.seq(alice) - aliceTicketSeq)); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); BEAST_EXPECT(env.seq(alice) == aliceSeq); env.require(balance(alice, USD(700))); @@ -1852,7 +1853,7 @@ class Check_test : public beast::unit_test::suite } // Operators to make using the class more convenient. - operator Account const() const + operator Account() const { return acct; } @@ -1879,7 +1880,7 @@ class Check_test : public beast::unit_test::suite // Automatic trust line creation should fail if the check destination // can't afford the reserve for the trust line. { - AccountOwns gw1{*this, env, "gw1", 0}; + AccountOwns const gw1{*this, env, "gw1", 0}; // Fund gw1 with noripple (even though that's atypical for a // gateway) so it does not have any flags set. We'll set flags @@ -1999,7 +2000,7 @@ class Check_test : public beast::unit_test::suite { // No account root flags on any participant. // Automatic trust line from issuer to destination. - AccountOwns gw1{*this, env, "gw1", 0}; + AccountOwns const gw1{*this, env, "gw1", 0}; BEAST_EXPECT((*env.le(gw1))[sfFlags] == 0); BEAST_EXPECT((*env.le(alice))[sfFlags] == 0); @@ -2052,7 +2053,7 @@ class Check_test : public beast::unit_test::suite // Transfer of assets using offers does not require rippling. // So bob's offer is successfully crossed which creates the // trust line. - AccountOwns gw1{*this, env, "gw1", 0}; + AccountOwns const gw1{*this, env, "gw1", 0}; IOU const OF1 = gw1["OF1"]; env(offer(alice, XRP(97), OF1(97))); env.close(); @@ -2101,7 +2102,7 @@ class Check_test : public beast::unit_test::suite { // gw1 enables rippling. // Automatic trust line from issuer to non-issuer should still work. - AccountOwns gw1{*this, env, "gw1", 0}; + AccountOwns const gw1{*this, env, "gw1", 0}; env(fset(gw1, asfDefaultRipple)); env.close(); @@ -2149,7 +2150,7 @@ class Check_test : public beast::unit_test::suite // to non-issuer should work. // Use offers to automatically create the trust line. - AccountOwns gw1{*this, env, "gw1", 0}; + AccountOwns const gw1{*this, env, "gw1", 0}; IOU const OF2 = gw1["OF2"]; env(offer(alice, XRP(95), OF2(95))); env.close(); @@ -2190,7 +2191,7 @@ class Check_test : public beast::unit_test::suite // change any outcomes. // // Automatic trust line from issuer to non-issuer should still work. - AccountOwns gw1{*this, env, "gw1", 0}; + AccountOwns const gw1{*this, env, "gw1", 0}; env(fset(gw1, asfDepositAuth)); env(fset(alice, asfDepositAuth)); env(fset(bob, asfDepositAuth)); @@ -2240,7 +2241,7 @@ class Check_test : public beast::unit_test::suite // automatic trust line creation. // Use offers to automatically create the trust line. - AccountOwns gw1{*this, env, "gw1", 0}; + AccountOwns const gw1{*this, env, "gw1", 0}; IOU const OF3 = gw1["OF3"]; env(offer(alice, XRP(93), OF3(93))); env.close(); @@ -2277,7 +2278,7 @@ class Check_test : public beast::unit_test::suite { // Set lsfGlobalFreeze on gw1. That should stop any automatic // trust lines from being created. - AccountOwns gw1{*this, env, "gw1", 0}; + AccountOwns const gw1{*this, env, "gw1", 0}; env(fset(gw1, asfGlobalFreeze)); env.close(); @@ -2319,7 +2320,7 @@ class Check_test : public beast::unit_test::suite // no automatic trust line creation between non-issuers. // Use offers to automatically create the trust line. - AccountOwns gw1{*this, env, "gw1", 0}; + AccountOwns const gw1{*this, env, "gw1", 0}; IOU const OF4 = gw1["OF4"]; env(offer(alice, XRP(91), OF4(91)), ter(tecFROZEN)); env.close(); @@ -2369,7 +2370,7 @@ class Check_test : public beast::unit_test::suite // Use offers to automatically create the trust line. IOU const OF5 = gw2["OF5"]; - std::uint32_t gw2OfferSeq = {env.seq(gw2)}; + std::uint32_t const gw2OfferSeq = {env.seq(gw2)}; env(offer(gw2, XRP(92), OF5(92))); ++gw2.owners; env.close(); @@ -2422,7 +2423,7 @@ class Check_test : public beast::unit_test::suite // no automatic trust line creation between non-issuers. // Use offers to automatically create the trust line. - AccountOwns gw2{*this, env, "gw2", 0}; + AccountOwns const gw2{*this, env, "gw2", 0}; IOU const OF5 = gw2["OF5"]; env(offer(alice, XRP(91), OF5(91)), ter(tecUNFUNDED_OFFER)); env.close(); diff --git a/src/test/app/Clawback_test.cpp b/src/test/app/Clawback_test.cpp index d169e9e165..902acf3222 100644 --- a/src/test/app/Clawback_test.cpp +++ b/src/test/app/Clawback_test.cpp @@ -52,7 +52,7 @@ class Clawback_test : public beast::unit_test::suite // Also, asfAllowTrustLineClawback cannot be cleared. { Env env(*this, features); - Account alice{"alice"}; + Account const alice{"alice"}; env.fund(XRP(1000), alice); env.close(); @@ -77,7 +77,7 @@ class Clawback_test : public beast::unit_test::suite // asfNoFreeze has been set { Env env(*this, features); - Account alice{"alice"}; + Account const alice{"alice"}; env.fund(XRP(1000), alice); env.close(); @@ -103,8 +103,8 @@ class Clawback_test : public beast::unit_test::suite { Env env(*this, features); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(1000), alice, bob); env.close(); @@ -146,7 +146,7 @@ class Clawback_test : public beast::unit_test::suite { Env env(*this, features - featureClawback); - Account alice{"alice"}; + Account const alice{"alice"}; env.fund(XRP(1000), alice); env.close(); @@ -183,8 +183,8 @@ class Clawback_test : public beast::unit_test::suite { Env env(*this, features - featureClawback); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(1000), alice, bob); env.close(); @@ -228,8 +228,8 @@ class Clawback_test : public beast::unit_test::suite { Env env(*this, features); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(1000), alice, bob); env.close(); @@ -310,8 +310,8 @@ class Clawback_test : public beast::unit_test::suite { Env env(*this, features); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; // bob's account is not funded and does not exist env.fund(XRP(1000), alice); @@ -332,9 +332,9 @@ class Clawback_test : public beast::unit_test::suite { Env env(*this, features); - Account alice{"alice"}; - Account bob{"bob"}; - Account cindy{"cindy"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const cindy{"cindy"}; env.fund(XRP(1000), alice, bob, cindy); env.close(); @@ -375,8 +375,8 @@ class Clawback_test : public beast::unit_test::suite { Env env(*this, features); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(1000), alice, bob); env.close(); @@ -445,8 +445,8 @@ class Clawback_test : public beast::unit_test::suite // Test that alice is able to successfully clawback tokens from bob Env env(*this, features); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(1000), alice, bob); env.close(); @@ -496,9 +496,9 @@ class Clawback_test : public beast::unit_test::suite { Env env(*this, features); - Account alice{"alice"}; - Account bob{"bob"}; - Account cindy{"cindy"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const cindy{"cindy"}; env.fund(XRP(1000), alice, bob, cindy); env.close(); @@ -554,9 +554,9 @@ class Clawback_test : public beast::unit_test::suite { Env env(*this, features); - Account alice{"alice"}; - Account bob{"bob"}; - Account cindy{"cindy"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const cindy{"cindy"}; env.fund(XRP(1000), alice, bob, cindy); env.close(); @@ -624,8 +624,8 @@ class Clawback_test : public beast::unit_test::suite // perspective is allowed to clawback Env env(*this, features); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(1000), alice, bob); env.close(); @@ -711,8 +711,8 @@ class Clawback_test : public beast::unit_test::suite // If clawback results the trustline to be default, // trustline should be automatically deleted Env env(*this, features); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(1000), alice, bob); env.close(); @@ -761,8 +761,8 @@ class Clawback_test : public beast::unit_test::suite // Claws back from frozen trustline // and the trustline should remain frozen Env env(*this, features); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(1000), alice, bob); env.close(); @@ -807,8 +807,8 @@ class Clawback_test : public beast::unit_test::suite // When alice tries to claw back an amount that is greater // than what bob holds, only the max available balance is clawed Env env(*this, features); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(1000), alice, bob); env.close(); @@ -859,8 +859,8 @@ class Clawback_test : public beast::unit_test::suite // Tests clawback with tickets Env env(*this, features); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(1000), alice, bob); env.close(); diff --git a/src/test/app/Credentials_test.cpp b/src/test/app/Credentials_test.cpp index 3c3d8b6df8..317fd47b2b 100644 --- a/src/test/app/Credentials_test.cpp +++ b/src/test/app/Credentials_test.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include @@ -471,7 +471,7 @@ struct Credentials_test : public beast::unit_test::suite { testcase("Credentials fail, expiration in the past."); auto jv = credentials::create(subject, issuer, credType); - // current time in ripple epoch - 1s + // current time in XRPL epoch - 1s uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count() - 1; jv[sfExpiration.jsonName] = t; @@ -522,6 +522,7 @@ struct Credentials_test : public beast::unit_test::suite directory::adjustOwnerNode); BEAST_EXPECT(res1); + // NOLINTNEXTLINE(readability-suspicious-call-argument) auto const jv = credentials::create(issuer, subject, credType); env(jv, ter(tecDIR_FULL)); // Free one directory entry by using a ticket @@ -811,7 +812,7 @@ struct Credentials_test : public beast::unit_test::suite testcase("CredentialsDelete fail, time not expired yet."); auto jv = credentials::create(subject, issuer, credType); - // current time in ripple epoch + 1000s + // current time in XRPL epoch + 1000s uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count() + 1000; jv[sfExpiration.jsonName] = t; diff --git a/src/test/app/DID_test.cpp b/src/test/app/DID_test.cpp index f83571deab..20c367d64f 100644 --- a/src/test/app/DID_test.cpp +++ b/src/test/app/DID_test.cpp @@ -189,7 +189,7 @@ struct DID_test : public beast::unit_test::suite Account const edna{"edna"}; Account const francis{"francis"}; Account const george{"george"}; - env.fund(XRP(5000), alice, bob, charlie, dave, edna, francis); + env.fund(XRP(5000), alice, bob, charlie, dave, edna, francis, george); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(ownerCount(env, bob) == 0); @@ -355,12 +355,14 @@ struct DID_test : public beast::unit_test::suite testAccountReserve(all); testSetInvalid(all); testDeleteInvalid(all); + testSetValidInitial(all); testSetModify(all); testEnabled(all - emptyDID); testAccountReserve(all - emptyDID); testSetInvalid(all - emptyDID); testDeleteInvalid(all - emptyDID); + testSetValidInitial(all - emptyDID); testSetModify(all - emptyDID); } }; diff --git a/src/test/app/DNS_test.cpp b/src/test/app/DNS_test.cpp index f478bc89ee..2273a77e09 100644 --- a/src/test/app/DNS_test.cpp +++ b/src/test/app/DNS_test.cpp @@ -15,7 +15,7 @@ class DNS_test : public beast::unit_test::suite using endpoint_type = boost::asio::ip::tcp::endpoint; using error_code = boost::system::error_code; std::weak_ptr work_; - endpoint_type lastEndpoint_{}; + endpoint_type lastEndpoint_; parsedURL pUrl_; std::string port_; jtx::Env env_; @@ -33,7 +33,7 @@ public: { auto onFetch = [&](error_code const& errorCode, endpoint_type const& endpoint, - xrpl::detail::response_type&& resp) { + xrpl::detail::response_type const& resp) { BEAST_EXPECT(!errorCode); lastEndpoint_ = endpoint; resolved_[endpoint.address().to_string()]++; @@ -62,7 +62,7 @@ public: { using boost::asio::ip::tcp; tcp::resolver resolver(env_.app().getIOContext()); - std::string port = pUrl_.port ? std::to_string(*pUrl_.port) : "443"; + std::string const port = pUrl_.port ? std::to_string(*pUrl_.port) : "443"; auto results = resolver.resolve(pUrl_.domain, port); auto it = results.begin(); auto end = results.end(); @@ -76,7 +76,7 @@ public: parse() { std::string url = arg(); - if (url == "") + if (url.empty()) url = "https://vl.ripple.com"; BEAST_EXPECT(parseUrl(pUrl_, url)); port_ = pUrl_.port ? std::to_string(*pUrl_.port) : "443"; diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp index 31a394eeeb..6c5698a227 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -16,9 +16,9 @@ class Delegate_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; - Account gw{"gateway"}; - Account alice{"alice"}; - Account bob{"bob"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(1000000), gw, alice, bob); env.close(); @@ -39,17 +39,15 @@ class Delegate_test : public beast::unit_test::suite using namespace jtx; Env env(*this); - Account gw{"gateway"}; - Account alice{"alice"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; env.fund(XRP(100000), gw, alice); env.close(); // delegating an empty permission list when the delegate ledger object - // does not exist will not create the ledger object - env(delegate::set(gw, alice, std::vector{})); + // does not exist is not allowed + env(delegate::set(gw, alice, {}), ter(tecNO_ENTRY)); env.close(); - auto const entry = delegate::entry(env, gw, alice); - BEAST_EXPECT(entry[jss::result][jss::error] == "entryNotFound"); auto const permissions = std::vector{ "Payment", "EscrowCreate", "EscrowFinish", "TrustlineAuthorize", "CheckCreate"}; @@ -92,9 +90,7 @@ class Delegate_test : public beast::unit_test::suite // newPermissions comparePermissions(delegate::entry(env, gw, alice), newPermissions, gw, alice); - // gw deletes all permissions delegated to alice, this will delete - // the - // ledger entry + // gw deletes all permissions delegated to alice, this will delete the ledger entry env(delegate::set(gw, alice, {})); env.close(); auto const jle = delegate::entry(env, gw, alice); @@ -116,9 +112,9 @@ class Delegate_test : public beast::unit_test::suite using namespace jtx; Env env(*this, features); - Account gw{"gateway"}; - Account alice{"alice"}; - Account bob{"bob"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(100000), gw, alice, bob); env.close(); @@ -204,34 +200,55 @@ class Delegate_test : public beast::unit_test::suite testcase("test reserve"); using namespace jtx; - // test reserve for DelegateSet + // reserve requirement not met { Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; - Account carol{"carol"}; + Account const alice{"alice"}; + Account const bob{"bob"}; - env.fund(drops(env.current()->fees().accountReserve(0)), alice); - env.fund(drops(env.current()->fees().accountReserve(1)), bob, carol); + auto const txFee = env.current()->fees().base; + env.fund(env.current()->fees().accountReserve(0) + txFee, alice); + env.fund(XRP(100000), bob); env.close(); // alice does not have enough reserve to create Delegate env(delegate::set(alice, bob, {"Payment"}), ter(tecINSUFFICIENT_RESERVE)); + } - // bob has enough reserve - env(delegate::set(bob, alice, {"Payment"})); + // reserve recovered after deleting delegation object + { + Env env(*this); + Account const bob{"bob"}; + Account const alice{"alice"}; + Account const carol{"carol"}; + + auto const txFee = env.current()->fees().base; + + env.fund(env.current()->fees().accountReserve(1) + (txFee * 4), alice); + env.fund(XRP(100000), bob, carol); env.close(); - // now bob create another Delegate, he does not have - // enough reserve - env(delegate::set(bob, carol, {"Payment"}), ter(tecINSUFFICIENT_RESERVE)); + // alice consumes 1 txFee and requires 1 object reserve + env(delegate::set(alice, bob, {"Payment"})); + env.close(); + + // alice does not have enough reserve to create another delegation object + env(delegate::set(alice, carol, {"Payment"}), ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + // deleting delegation object recovers 1 reserve + env(delegate::set(alice, bob, {})); + env.close(); + + // now alice can delegate again + env(delegate::set(alice, carol, {"Payment"})); } // test reserve when sending transaction on behalf of other account { Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(drops(env.current()->fees().accountReserve(1)), alice); env.fund(drops(env.current()->fees().accountReserve(2)), bob); @@ -257,37 +274,42 @@ class Delegate_test : public beast::unit_test::suite testcase("test fee"); using namespace jtx; - Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; - Account carol{"carol"}; - env.fund(XRP(10000), alice, carol); - env.fund(XRP(1000), bob); - env.close(); + // Common setup: fund alice, bob, carol with 1000 XRP. + auto setup = [&](Env& env) { + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; + env.fund(XRP(1000), alice, bob, carol); + env.close(); + return std::make_tuple(alice, bob, carol); + }; + // No fee deduction for terNO_DELEGATE_PERMISSION. { - auto aliceBalance = env.balance(alice); - auto bobBalance = env.balance(bob); - auto carolBalance = env.balance(carol); + Env env(*this); + auto [alice, bob, carol] = setup(env); - env(pay(alice, carol, XRP(100)), - fee(XRP(2000)), - delegate::as(bob), - ter(terNO_DELEGATE_PERMISSION)); + auto const aliceBalance = env.balance(alice); + auto const bobBalance = env.balance(bob); + auto const carolBalance = env.balance(carol); + + env(pay(alice, carol, XRP(100)), delegate::as(bob), ter(terNO_DELEGATE_PERMISSION)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance); BEAST_EXPECT(env.balance(bob) == bobBalance); BEAST_EXPECT(env.balance(carol) == carolBalance); } - env(delegate::set(alice, bob, {"Payment"})); - env.close(); - + // Delegate pays the fee successfully. { - // Delegate pays the fee - auto aliceBalance = env.balance(alice); - auto bobBalance = env.balance(bob); - auto carolBalance = env.balance(carol); + Env env(*this); + auto [alice, bob, carol] = setup(env); + env(delegate::set(alice, bob, {"Payment"})); + env.close(); + + auto const aliceBalance = env.balance(alice); + auto const bobBalance = env.balance(bob); + auto const carolBalance = env.balance(carol); auto const sendAmt = XRP(100); auto const feeAmt = XRP(10); @@ -298,11 +320,16 @@ class Delegate_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(carol) == carolBalance + sendAmt); } + // Bob has insufficient balance to pay the fee, will get terINSUF_FEE_B. { - // insufficient balance to pay fee - auto aliceBalance = env.balance(alice); - auto bobBalance = env.balance(bob); - auto carolBalance = env.balance(carol); + Env env(*this); + auto [alice, bob, carol] = setup(env); + env(delegate::set(alice, bob, {"Payment"})); + env.close(); + + auto const aliceBalance = env.balance(alice); + auto const bobBalance = env.balance(bob); + auto const carolBalance = env.balance(carol); env(pay(alice, carol, XRP(100)), fee(XRP(2000)), @@ -314,22 +341,143 @@ class Delegate_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(carol) == carolBalance); } + // The delegated account has enough balance to pay and delegator has enough reserve { - // fee is paid by Delegate - // on context reset (tec error) - auto aliceBalance = env.balance(alice); - auto bobBalance = env.balance(bob); - auto carolBalance = env.balance(carol); - auto const feeAmt = XRP(10); + // Common setup: fund accounts and grant Bob permission to pay on Alice's behalf. + // Alice is funded with exactly (paymentAmount + reserve + baseFee): baseFee covers + // the DelegateSet tx cost, leaving Alice with exactly (paymentAmount + reserve). + // highFee = reserve + baseFee, strictly greater than reserve, so that + // max(reserve, highFee) = highFee — making the direct payment check fail. + auto setup = [&](Env& env) { + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; - env(pay(alice, carol, XRP(20000)), - fee(feeAmt), - delegate::as(bob), - ter(tecUNFUNDED_PAYMENT)); + auto const baseFee = env.current()->fees().base; + auto const reserve = env.current()->fees().accountReserve(1); + auto const paymentAmount = XRP(1); + auto const highFee = reserve + baseFee; + BEAST_EXPECT(highFee > reserve); + + env.fund(paymentAmount + reserve + baseFee, alice); + env.fund(XRP(1000), bob); + env.fund(XRP(1000), carol); + env.close(); + + env(delegate::set(alice, bob, {"Payment"})); + env.close(); + + env.require(balance(alice, paymentAmount + reserve)); + + return std::make_tuple(alice, bob, carol, paymentAmount, highFee, reserve); + }; + + // Alice's balance (paymentAmount + reserve) is insufficient to cover both + // the payment and highFee directly. Even though fees are allowed to dip + // below reserve, when Alice pays the fee herself the required funds = + // paymentAmount + max(reserve, highFee) = paymentAmount + highFee + // (since highFee > reserve), which still exceeds her balance. + // tec: highFee is consumed from Alice's balance. + { + Env env(*this); + auto [alice, bob, carol, paymentAmount, highFee, reserve] = setup(env); + auto const aliceBalance = env.balance(alice); + auto const bobBalance = env.balance(bob); + auto const carolBalance = env.balance(carol); + + env(pay(alice, carol, paymentAmount), fee(highFee), ter(tecUNFUNDED_PAYMENT)); + + // tec consumes the fee from Alice; carol and bob are unaffected. + BEAST_EXPECT(env.balance(alice) == aliceBalance - highFee); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(carol) == carolBalance); + } + + // The payment succeeds because the delegated account pays the fee. + // Alice only needs (paymentAmount + reserve). + { + Env env(*this); + auto [alice, bob, carol, paymentAmount, highFee, reserve] = setup(env); + + auto const alicePrePay = env.balance(alice, XRP); + auto const bobPrePay = env.balance(bob, XRP); + auto const carolPrePay = env.balance(carol, XRP); + + env(pay(alice, carol, paymentAmount), delegate::as(bob), fee(highFee)); + env.close(); + + env.require(balance(alice, alicePrePay - paymentAmount)); + env.require(balance(bob, bobPrePay - highFee)); + env.require(balance(carol, carolPrePay + paymentAmount)); + } + } + + // Delegated account can pay the fee even if it dips below reserve. + { + Env env(*this); + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; + + auto const baseFee = env.current()->fees().base; + auto const baseReserve = env.current()->fees().accountReserve(0); + + env.fund(env.current()->fees().accountReserve(1) + baseFee + XRP(1), alice); + env.fund(baseReserve, bob); + env.fund(XRP(1000), carol); env.close(); - BEAST_EXPECT(env.balance(alice) == aliceBalance); - BEAST_EXPECT(env.balance(bob) == bobBalance - feeAmt); - BEAST_EXPECT(env.balance(carol) == carolBalance); + + env(delegate::set(alice, bob, {"Payment"})); + env.close(); + + auto const alicePreTx = env.balance(alice, XRP); + auto const bobPreTx = env.balance(bob, XRP); + + // After paying for this transaction, bob's balance will + // dip below the base reserve + env(pay(alice, carol, XRP(1)), delegate::as(bob)); + env.close(); + + // Bob's balance is now less than the base reserve. + BEAST_EXPECT(env.balance(bob, XRP) < baseReserve); + env.require(balance(bob, bobPreTx - drops(baseFee))); + + // Alice's balance only decreased by the 1.0 XRP she sent. + env.require(balance(alice, alicePreTx - XRP(1))); + } + + // The delegated account has enough balance for the fee, but delegator + // runs into tecUNFUNDED_PAYMENT. + { + Env env(*this); + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; + + auto const baseFee = env.current()->fees().base; + auto const reserve = env.current()->fees().accountReserve(1); + + // Alice is funded with (reserve + baseFee): after DelegateSet she has + // exactly 'reserve', which is insufficient to send XRP(10) while keeping + // reserve. Bob has plenty to pay the fee. + env.fund(reserve + baseFee, alice); + env.fund(XRP(1000), bob); + env.fund(XRP(1000), carol); + env.close(); + + env(delegate::set(alice, bob, {"Payment"})); + env.close(); + + auto const alicePrePay = env.balance(alice, XRP); + auto const bobPrePay = env.balance(bob, XRP); + auto const carolPrePay = env.balance(carol, XRP); + + // Bob pays the fee, but Alice has insufficient balance to send XRP(10). + env(pay(alice, carol, XRP(10)), delegate::as(bob), ter(tecUNFUNDED_PAYMENT)); + + env.require(balance(alice, alicePrePay)); + env.require(balance(bob, bobPrePay - drops(baseFee))); + env.require(balance(carol, carolPrePay)); } } @@ -340,9 +488,9 @@ class Delegate_test : public beast::unit_test::suite using namespace jtx; Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; - Account carol{"carol"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; env.fund(XRP(10000), alice, bob, carol); env.close(); @@ -393,8 +541,8 @@ class Delegate_test : public beast::unit_test::suite using namespace jtx; Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(100000), alice, bob); env.close(); @@ -427,9 +575,9 @@ class Delegate_test : public beast::unit_test::suite using namespace jtx; Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; - Account carol{"carol"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; XRPAmount const baseFee{env.current()->fees().base}; @@ -493,10 +641,10 @@ class Delegate_test : public beast::unit_test::suite // test PaymentMint and PaymentBurn { Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; - Account gw{"gateway"}; - Account gw2{"gateway2"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const gw{"gateway"}; + Account const gw2{"gateway2"}; auto const USD = gw["USD"]; auto const EUR = gw2["EUR"]; @@ -598,9 +746,9 @@ class Delegate_test : public beast::unit_test::suite // test PaymentMint won't affect Payment transaction level delegation. { Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; - Account gw{"gateway"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const gw{"gateway"}; auto const USD = gw["USD"]; env.fund(XRP(10000), alice); @@ -788,9 +936,9 @@ class Delegate_test : public beast::unit_test::suite // test TrustlineUnfreeze, TrustlineFreeze and TrustlineAuthorize { Env env(*this); - Account gw{"gw"}; - Account alice{"alice"}; - Account bob{"bob"}; + Account const gw{"gw"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(10000), gw, alice, bob); env(fset(gw, asfRequireAuth)); env.close(); @@ -900,9 +1048,9 @@ class Delegate_test : public beast::unit_test::suite // test mix of transaction level delegation and granular delegation { Env env(*this); - Account gw{"gw"}; - Account alice{"alice"}; - Account bob{"bob"}; + Account const gw{"gw"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(10000), gw, alice, bob); env(fset(gw, asfRequireAuth)); env.close(); @@ -945,9 +1093,9 @@ class Delegate_test : public beast::unit_test::suite // tfFullyCanonicalSig won't block delegated transaction { Env env(*this); - Account gw{"gw"}; - Account alice{"alice"}; - Account bob{"bob"}; + Account const gw{"gw"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(10000), gw, alice, bob); env(fset(gw, asfRequireAuth)); env.close(); @@ -1193,8 +1341,8 @@ class Delegate_test : public beast::unit_test::suite // tfFullyCanonicalSig won't block delegated transaction { Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(10000), alice, bob); env.close(); @@ -1221,8 +1369,8 @@ class Delegate_test : public beast::unit_test::suite // test MPTokenIssuanceUnlock and MPTokenIssuanceLock permissions { Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(100000), alice, bob); env.close(); @@ -1268,8 +1416,8 @@ class Delegate_test : public beast::unit_test::suite // test mix of granular and transaction level permission { Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(100000), alice, bob); env.close(); @@ -1315,8 +1463,8 @@ class Delegate_test : public beast::unit_test::suite // tfFullyCanonicalSig won't block delegated transaction { Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(100000), alice, bob); env.close(); @@ -1339,9 +1487,9 @@ class Delegate_test : public beast::unit_test::suite using namespace jtx; Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; - Account carol{"carol"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; env.fund(XRP(100000), alice, bob, carol); env.close(); @@ -1367,9 +1515,9 @@ class Delegate_test : public beast::unit_test::suite { Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; - Account carol{"carol"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; env.fund(XRP(100000), alice, bob, carol); env.close(); @@ -1393,7 +1541,9 @@ class Delegate_test : public beast::unit_test::suite { Env env(*this); - Account alice{"alice"}, bob{"bob"}, carol{"carol"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; env.fund(XRP(100000), alice, bob, carol); env.close(); @@ -1427,7 +1577,9 @@ class Delegate_test : public beast::unit_test::suite { Env env(*this); - Account alice{"alice"}, bob{"bob"}, carol{"carol"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; env.fund(XRP(100000), alice, bob, carol); env.close(); @@ -1464,9 +1616,9 @@ class Delegate_test : public beast::unit_test::suite using namespace jtx; Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; - Account carol{"carol"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; Account daria{"daria"}; Account edward{"edward"}; env.fund(XRP(100000), alice, bob, carol, daria, edward); @@ -1500,9 +1652,9 @@ class Delegate_test : public beast::unit_test::suite using namespace jtx; Env env(*this); - Account alice{"alice"}; - Account bob{"bob"}; - Account carol{"carol"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; Account daria = Account{"daria"}; Account edward = Account{"edward"}; Account fred = Account{"fred"}; @@ -1542,8 +1694,8 @@ class Delegate_test : public beast::unit_test::suite Env env(*this, features); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(100000), alice, bob); env.close(); @@ -1614,13 +1766,7 @@ class Delegate_test : public beast::unit_test::suite {"CredentialDelete", featureCredentials}, {"NFTokenModify", featureDynamicNFT}, {"PermissionedDomainSet", featurePermissionedDomains}, - {"PermissionedDomainDelete", featurePermissionedDomains}, - {"VaultCreate", featureSingleAssetVault}, - {"VaultSet", featureSingleAssetVault}, - {"VaultDelete", featureSingleAssetVault}, - {"VaultDeposit", featureSingleAssetVault}, - {"VaultWithdraw", featureSingleAssetVault}, - {"VaultClawback", featureSingleAssetVault}}; + {"PermissionedDomainDelete", featurePermissionedDomains}}; // Can not delegate tx if any required feature disabled. { @@ -1660,6 +1806,56 @@ class Delegate_test : public beast::unit_test::suite } } + void + testTxDelegableCount() + { + testcase("Delegable Transactions Completeness"); + + std::size_t delegableCount = 0; + +#pragma push_macro("TRANSACTION") +#undef TRANSACTION + +#define TRANSACTION(tag, value, name, delegable, ...) \ + if (delegable == xrpl::delegable) \ + { \ + delegableCount++; \ + } + +#include + +#undef TRANSACTION +#pragma pop_macro("TRANSACTION") + + // ==================================================================== + // IMPORTANT NOTICE: + // + // If this test fails, it indicates that the 'Delegation::delegable' status + // in transactions.macro has been changed. Delegation allows accounts to act + // on behalf of others, significantly increasing the security surface. + // + // + // To ENSURE any added transaction is safe and compatible with delegation: + // + // 1. Verify that the transaction is intended to be delegable. + // 2. Every standard test case for that transaction MUST be + // duplicated and verified for a Delegated context. + // 3. Ensure that Fee, Reserve, and Signing are correctly handled. + // + // DO NOT modify expectedDelegableCount unless all scenarios, including + // edge cases, have been fully tested and verified. + // ==================================================================== + std::size_t const expectedDelegableCount = 75; + + BEAST_EXPECTS( + delegableCount == expectedDelegableCount, + "\n[SECURITY] New delegable transaction detected!" + "\n Expected: " + + std::to_string(expectedDelegableCount) + + "\n Actual: " + std::to_string(delegableCount) + + "\n Action: Verify security requirements to interact with Delegation feature"); + } + void run() override { @@ -1684,6 +1880,7 @@ class Delegate_test : public beast::unit_test::suite testMultiSignQuorumNotMet(); testPermissionValue(all); testTxRequireFeatures(all); + testTxDelegableCount(); } }; BEAST_DEFINE_TESTSUITE(Delegate, app, xrpl); diff --git a/src/test/app/DepositAuth_test.cpp b/src/test/app/DepositAuth_test.cpp index 547d9d8b62..9b5dca3ca2 100644 --- a/src/test/app/DepositAuth_test.cpp +++ b/src/test/app/DepositAuth_test.cpp @@ -304,9 +304,11 @@ struct DepositAuth_test : public beast::unit_test::suite auto const noRipplePrev = i & 0x1; auto const noRippleNext = i & 0x2; auto const withDepositAuth = i & 0x4; - testIssuer(testable_amendments(), noRipplePrev, noRippleNext, withDepositAuth); + testIssuer( + testable_amendments(), noRipplePrev != 0, noRippleNext != 0, withDepositAuth != 0); - testNonIssuer(testable_amendments(), noRipplePrev, noRippleNext, withDepositAuth); + testNonIssuer( + testable_amendments(), noRipplePrev != 0, noRippleNext != 0, withDepositAuth != 0); } } @@ -1043,7 +1045,7 @@ struct DepositPreauth_test : public beast::unit_test::suite // Create credentials auto jv = credentials::create(alice, issuer, credType); - // Current time in ripple epoch. + // Current time in XRPL epoch. // Every time ledger close, unittest timer increase by 10s uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count() + 60; @@ -1283,8 +1285,10 @@ struct DepositPreauth_test : public beast::unit_test::suite auto issuer = c[jss::Issuer].asString(); if (BEAST_EXPECT(pubKey2Acc.contains(issuer))) + { readCreds.emplace_back( pubKey2Acc.at(issuer), c["CredentialType"].asString()); + } } BEAST_EXPECT(std::ranges::is_sorted(readCreds)); diff --git a/src/test/app/Discrepancy_test.cpp b/src/test/app/Discrepancy_test.cpp index 08a8a01753..d8c14df64e 100644 --- a/src/test/app/Discrepancy_test.cpp +++ b/src/test/app/Discrepancy_test.cpp @@ -24,13 +24,13 @@ class Discrepancy_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this, features}; - Account A1{"A1"}; - Account A2{"A2"}; - Account A3{"A3"}; - Account A4{"A4"}; - Account A5{"A5"}; - Account A6{"A6"}; - Account A7{"A7"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const A3{"A3"}; + Account const A4{"A4"}; + Account const A5{"A5"}; + Account const A6{"A6"}; + Account const A7{"A7"}; env.fund(XRP(2000), A1); env.fund(XRP(1000), A2, A6, A7); @@ -68,7 +68,7 @@ class Discrepancy_test : public beast::unit_test::suite env(offer(A7, XRP(1233), A6["CNY"](25))); env.close(); - test::PathSet payPaths{ + test::PathSet const payPaths{ test::Path{A2["JPY"], A2}, test::Path{XRP, A2["JPY"], A2}, test::Path{A6, XRP, A2["JPY"], A2}}; @@ -84,7 +84,7 @@ class Discrepancy_test : public beast::unit_test::suite jrq2[jss::transaction] = env.tx()->getJson(JsonOptions::none)[jss::hash]; jrq2[jss::id] = 3; auto jrr = env.rpc("json", "tx", to_string(jrq2))[jss::result]; - uint64_t fee{jrr[jss::Fee].asUInt()}; + uint64_t const fee{jrr[jss::Fee].asUInt()}; auto meta = jrr[jss::meta]; uint64_t sumPrev{0}; uint64_t sumFinal{0}; @@ -93,11 +93,17 @@ class Discrepancy_test : public beast::unit_test::suite { Json::Value node; if (an.isMember(sfCreatedNode.fieldName)) + { node = an[sfCreatedNode.fieldName]; + } else if (an.isMember(sfModifiedNode.fieldName)) + { node = an[sfModifiedNode.fieldName]; + } else if (an.isMember(sfDeletedNode.fieldName)) + { node = an[sfDeletedNode.fieldName]; + } if (node && node[sfLedgerEntryType.fieldName] == jss::AccountRoot) { @@ -108,11 +114,15 @@ class Discrepancy_test : public beast::unit_test::suite ? node[sfFinalFields.fieldName] : node[sfNewFields.fieldName]; if (prevFields) + { sumPrev += beast::lexicalCastThrow( prevFields[sfBalance.fieldName].asString()); + } if (finalFields) + { sumFinal += beast::lexicalCastThrow( finalFields[sfBalance.fieldName].asString()); + } } } // the difference in balances (final and prev) should be the diff --git a/src/test/app/EscrowToken_test.cpp b/src/test/app/EscrowToken_test.cpp index 5cbeb0b94a..6e08c3eddf 100644 --- a/src/test/app/EscrowToken_test.cpp +++ b/src/test/app/EscrowToken_test.cpp @@ -35,7 +35,7 @@ struct EscrowToken_test : public beast::unit_test::suite return 0; } - jtx::PrettyAmount + static jtx::PrettyAmount issuerBalance(jtx::Env& env, jtx::Account const& account, Issue const& issue) { Json::Value params; @@ -49,7 +49,7 @@ struct EscrowToken_test : public beast::unit_test::suite return {amount, account.name()}; } - jtx::PrettyAmount + static jtx::PrettyAmount issuerEscrowed(jtx::Env& env, jtx::Account const& account, Issue const& issue) { Json::Value params; @@ -983,13 +983,13 @@ struct EscrowToken_test : public beast::unit_test::suite auto const aa = env.le(keylet::escrow(alice.id(), aseq)); BEAST_EXPECT(aa); { - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 2); BEAST_EXPECT(std::find(aod.begin(), aod.end(), aa) != aod.end()); } { - xrpl::Dir iod(*env.current(), keylet::ownerDir(gw.id())); + xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id())); BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 4); BEAST_EXPECT(std::find(iod.begin(), iod.end(), aa) != iod.end()); } @@ -1004,13 +1004,13 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT(bb); { - xrpl::Dir bod(*env.current(), keylet::ownerDir(bob.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bb) != bod.end()); } { - xrpl::Dir iod(*env.current(), keylet::ownerDir(gw.id())); + xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id())); BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 5); BEAST_EXPECT(std::find(iod.begin(), iod.end(), bb) != iod.end()); } @@ -1022,15 +1022,15 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT( (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1); BEAST_EXPECT(std::find(aod.begin(), aod.end(), aa) == aod.end()); - xrpl::Dir bod(*env.current(), keylet::ownerDir(bob.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bb) != bod.end()); - xrpl::Dir iod(*env.current(), keylet::ownerDir(gw.id())); + xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id())); BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 4); BEAST_EXPECT(std::find(iod.begin(), iod.end(), bb) != iod.end()); } @@ -1042,11 +1042,11 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT( (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); - xrpl::Dir bod(*env.current(), keylet::ownerDir(bob.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bb) == bod.end()); - xrpl::Dir iod(*env.current(), keylet::ownerDir(gw.id())); + xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id())); BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 3); BEAST_EXPECT(std::find(iod.begin(), iod.end(), bb) == iod.end()); } @@ -1085,20 +1085,20 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT(bc); { - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 2); BEAST_EXPECT(std::find(aod.begin(), aod.end(), ab) != aod.end()); - xrpl::Dir bod(*env.current(), keylet::ownerDir(bob.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 3); BEAST_EXPECT(std::find(bod.begin(), bod.end(), ab) != bod.end()); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bc) != bod.end()); - xrpl::Dir cod(*env.current(), keylet::ownerDir(carol.id())); + xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id())); BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 2); BEAST_EXPECT(std::find(cod.begin(), cod.end(), bc) != cod.end()); - xrpl::Dir iod(*env.current(), keylet::ownerDir(gw.id())); + xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id())); BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 5); BEAST_EXPECT(std::find(iod.begin(), iod.end(), ab) != iod.end()); BEAST_EXPECT(std::find(iod.begin(), iod.end(), bc) != iod.end()); @@ -1110,19 +1110,19 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq))); BEAST_EXPECT(env.le(keylet::escrow(bob.id(), bseq))); - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1); BEAST_EXPECT(std::find(aod.begin(), aod.end(), ab) == aod.end()); - xrpl::Dir bod(*env.current(), keylet::ownerDir(bob.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2); BEAST_EXPECT(std::find(bod.begin(), bod.end(), ab) == bod.end()); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bc) != bod.end()); - xrpl::Dir cod(*env.current(), keylet::ownerDir(carol.id())); + xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id())); BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 2); - xrpl::Dir iod(*env.current(), keylet::ownerDir(gw.id())); + xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id())); BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 4); BEAST_EXPECT(std::find(iod.begin(), iod.end(), ab) == iod.end()); BEAST_EXPECT(std::find(iod.begin(), iod.end(), bc) != iod.end()); @@ -1134,19 +1134,19 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq))); BEAST_EXPECT(!env.le(keylet::escrow(bob.id(), bseq))); - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1); BEAST_EXPECT(std::find(aod.begin(), aod.end(), ab) == aod.end()); - xrpl::Dir bod(*env.current(), keylet::ownerDir(bob.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1); BEAST_EXPECT(std::find(bod.begin(), bod.end(), ab) == bod.end()); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bc) == bod.end()); - xrpl::Dir cod(*env.current(), keylet::ownerDir(carol.id())); + xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id())); BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1); - xrpl::Dir iod(*env.current(), keylet::ownerDir(gw.id())); + xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id())); BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 3); BEAST_EXPECT(std::find(iod.begin(), iod.end(), ab) == iod.end()); BEAST_EXPECT(std::find(iod.begin(), iod.end(), bc) == iod.end()); @@ -1182,14 +1182,14 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT(ag); { - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 2); BEAST_EXPECT(std::find(aod.begin(), aod.end(), ag) != aod.end()); - xrpl::Dir cod(*env.current(), keylet::ownerDir(carol.id())); + xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id())); BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1); - xrpl::Dir iod(*env.current(), keylet::ownerDir(gw.id())); + xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id())); BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 3); BEAST_EXPECT(std::find(iod.begin(), iod.end(), ag) != iod.end()); } @@ -1199,14 +1199,14 @@ struct EscrowToken_test : public beast::unit_test::suite { BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq))); - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1); BEAST_EXPECT(std::find(aod.begin(), aod.end(), ag) == aod.end()); - xrpl::Dir cod(*env.current(), keylet::ownerDir(carol.id())); + xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id())); BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1); - xrpl::Dir iod(*env.current(), keylet::ownerDir(gw.id())); + xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id())); BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 2); BEAST_EXPECT(std::find(iod.begin(), iod.end(), ag) == iod.end()); } @@ -1229,7 +1229,7 @@ struct EscrowToken_test : public beast::unit_test::suite bool negative; }; - std::array tests = {{ + std::array const tests = {{ // src > dst && src > issuer && dst no trustline {Account("alice2"), Account("bob0"), Account{"gw0"}, false, true}, // src < dst && src < issuer && dst no trustline @@ -1258,9 +1258,13 @@ struct EscrowToken_test : public beast::unit_test::suite env.close(); if (t.hasTrustline) + { env.trust(USD(100'000), t.src, t.dst); + } else + { env.trust(USD(100'000), t.src); + } env.close(); env(pay(t.gw, t.src, USD(10'000))); @@ -1331,7 +1335,7 @@ struct EscrowToken_test : public beast::unit_test::suite env.close(); } - std::array gwDstTests = {{ + std::array const gwDstTests = {{ // src > dst && src > issuer && dst has trustline {Account("alice2"), Account{"gw0"}, true}, // src < dst && src < issuer && dst has trustline @@ -2514,11 +2518,11 @@ struct EscrowToken_test : public beast::unit_test::suite env.close(); auto const seq1 = env.seq(alice); - env.app().openLedger().modify([&](OpenView& view, beast::Journal j) { + env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) { Sandbox sb(&view, tapNONE); auto sleNew = std::make_shared(keylet::escrow(alice, seq1)); MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}}; - STAmount amt(mpt, 10); + STAmount const amt(mpt, 10); sleNew->setAccountID(sfDestination, bob); sleNew->setFieldAmount(sfAmount, amt); sb.insert(sleNew); @@ -2741,11 +2745,11 @@ struct EscrowToken_test : public beast::unit_test::suite env.fund(XRP(10'000), alice, bob); auto const seq1 = env.seq(alice); - env.app().openLedger().modify([&](OpenView& view, beast::Journal j) { + env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) { Sandbox sb(&view, tapNONE); auto sleNew = std::make_shared(keylet::escrow(alice, seq1)); MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}}; - STAmount amt(mpt, 10); + STAmount const amt(mpt, 10); sleNew->setAccountID(sfDestination, bob); sleNew->setFieldAmount(sfAmount, amt); sb.insert(sleNew); @@ -3096,13 +3100,13 @@ struct EscrowToken_test : public beast::unit_test::suite auto const aa = env.le(keylet::escrow(alice.id(), aseq)); BEAST_EXPECT(aa); { - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 2); BEAST_EXPECT(std::find(aod.begin(), aod.end(), aa) != aod.end()); } { - xrpl::Dir iod(*env.current(), keylet::ownerDir(gw.id())); + xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id())); BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 1); BEAST_EXPECT(std::find(iod.begin(), iod.end(), aa) == iod.end()); } @@ -3117,7 +3121,7 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT(bb); { - xrpl::Dir bod(*env.current(), keylet::ownerDir(bob.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bb) != bod.end()); } @@ -3129,11 +3133,11 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT( (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1); BEAST_EXPECT(std::find(aod.begin(), aod.end(), aa) == aod.end()); - xrpl::Dir bod(*env.current(), keylet::ownerDir(bob.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bb) != bod.end()); } @@ -3145,7 +3149,7 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT( (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); - xrpl::Dir bod(*env.current(), keylet::ownerDir(bob.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bb) == bod.end()); } @@ -3187,16 +3191,16 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT(bc); { - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 2); BEAST_EXPECT(std::find(aod.begin(), aod.end(), ab) != aod.end()); - xrpl::Dir bod(*env.current(), keylet::ownerDir(bob.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 3); BEAST_EXPECT(std::find(bod.begin(), bod.end(), ab) != bod.end()); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bc) != bod.end()); - xrpl::Dir cod(*env.current(), keylet::ownerDir(carol.id())); + xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id())); BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 2); BEAST_EXPECT(std::find(cod.begin(), cod.end(), bc) != cod.end()); } @@ -3207,16 +3211,16 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq))); BEAST_EXPECT(env.le(keylet::escrow(bob.id(), bseq))); - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1); BEAST_EXPECT(std::find(aod.begin(), aod.end(), ab) == aod.end()); - xrpl::Dir bod(*env.current(), keylet::ownerDir(bob.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2); BEAST_EXPECT(std::find(bod.begin(), bod.end(), ab) == bod.end()); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bc) != bod.end()); - xrpl::Dir cod(*env.current(), keylet::ownerDir(carol.id())); + xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id())); BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 2); } @@ -3226,16 +3230,16 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq))); BEAST_EXPECT(!env.le(keylet::escrow(bob.id(), bseq))); - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1); BEAST_EXPECT(std::find(aod.begin(), aod.end(), ab) == aod.end()); - xrpl::Dir bod(*env.current(), keylet::ownerDir(bob.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1); BEAST_EXPECT(std::find(bod.begin(), bod.end(), ab) == bod.end()); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bc) == bod.end()); - xrpl::Dir cod(*env.current(), keylet::ownerDir(carol.id())); + xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id())); BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1); } } diff --git a/src/test/app/Escrow_test.cpp b/src/test/app/Escrow_test.cpp index 4538f2d751..05640cde01 100644 --- a/src/test/app/Escrow_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -1057,7 +1057,7 @@ struct Escrow_test : public beast::unit_test::suite Env env(*this, features); env.fund(XRP(5000), "alice", "bob"); - std::array cb = { + std::array const cb = { {0xA2, 0x2B, 0x80, 0x20, 0x42, 0x4A, 0x70, 0x49, 0x49, 0x52, 0x92, 0x67, 0xB6, 0x21, 0xB3, 0xD7, 0x91, 0x19, 0xD7, 0x29, 0xB2, 0x38, 0x2C, 0xED, 0x8B, 0x29, 0x6C, 0x3C, 0x02, 0x8F, 0xA9, 0x7D, 0x35, 0x0F, 0x6D, 0x07, @@ -1100,7 +1100,7 @@ struct Escrow_test : public beast::unit_test::suite BEAST_EXPECT(aa); { - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1); BEAST_EXPECT(std::find(aod.begin(), aod.end(), aa) != aod.end()); } @@ -1115,7 +1115,7 @@ struct Escrow_test : public beast::unit_test::suite BEAST_EXPECT(bb); { - xrpl::Dir bod(*env.current(), keylet::ownerDir(bruce.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bruce.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bb) != bod.end()); } @@ -1127,11 +1127,11 @@ struct Escrow_test : public beast::unit_test::suite BEAST_EXPECT( (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 0); BEAST_EXPECT(std::find(aod.begin(), aod.end(), aa) == aod.end()); - xrpl::Dir bod(*env.current(), keylet::ownerDir(bruce.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bruce.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bb) != bod.end()); } @@ -1143,7 +1143,7 @@ struct Escrow_test : public beast::unit_test::suite BEAST_EXPECT( (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); - xrpl::Dir bod(*env.current(), keylet::ownerDir(bruce.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bruce.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 0); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bb) == bod.end()); } @@ -1174,16 +1174,16 @@ struct Escrow_test : public beast::unit_test::suite BEAST_EXPECT(bc); { - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1); BEAST_EXPECT(std::find(aod.begin(), aod.end(), ab) != aod.end()); - xrpl::Dir bod(*env.current(), keylet::ownerDir(bruce.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bruce.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2); BEAST_EXPECT(std::find(bod.begin(), bod.end(), ab) != bod.end()); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bc) != bod.end()); - xrpl::Dir cod(*env.current(), keylet::ownerDir(carol.id())); + xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id())); BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1); BEAST_EXPECT(std::find(cod.begin(), cod.end(), bc) != cod.end()); } @@ -1194,16 +1194,16 @@ struct Escrow_test : public beast::unit_test::suite BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq))); BEAST_EXPECT(env.le(keylet::escrow(bruce.id(), bseq))); - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 0); BEAST_EXPECT(std::find(aod.begin(), aod.end(), ab) == aod.end()); - xrpl::Dir bod(*env.current(), keylet::ownerDir(bruce.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bruce.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1); BEAST_EXPECT(std::find(bod.begin(), bod.end(), ab) == bod.end()); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bc) != bod.end()); - xrpl::Dir cod(*env.current(), keylet::ownerDir(carol.id())); + xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id())); BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1); } @@ -1213,16 +1213,16 @@ struct Escrow_test : public beast::unit_test::suite BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq))); BEAST_EXPECT(!env.le(keylet::escrow(bruce.id(), bseq))); - xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); + xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 0); BEAST_EXPECT(std::find(aod.begin(), aod.end(), ab) == aod.end()); - xrpl::Dir bod(*env.current(), keylet::ownerDir(bruce.id())); + xrpl::Dir const bod(*env.current(), keylet::ownerDir(bruce.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 0); BEAST_EXPECT(std::find(bod.begin(), bod.end(), ab) == bod.end()); BEAST_EXPECT(std::find(bod.begin(), bod.end(), bc) == bod.end()); - xrpl::Dir cod(*env.current(), keylet::ownerDir(carol.id())); + xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id())); BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 0); } } @@ -1250,7 +1250,7 @@ struct Escrow_test : public beast::unit_test::suite fee(baseFee)); auto const pf = preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); - BEAST_EXPECT(pf.ter == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(baseFee)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(1000)); @@ -1260,7 +1260,7 @@ struct Escrow_test : public beast::unit_test::suite auto const jtx = env.jt(escrow::cancel("bob", "alice", 3), seq(1), fee(baseFee)); auto const pf = preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); - BEAST_EXPECT(pf.ter == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(baseFee)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0)); @@ -1270,7 +1270,7 @@ struct Escrow_test : public beast::unit_test::suite auto const jtx = env.jt(escrow::finish("bob", "alice", 3), seq(1), fee(baseFee)); auto const pf = preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); - BEAST_EXPECT(pf.ter == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(baseFee)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0)); diff --git a/src/test/app/FeeVote_test.cpp b/src/test/app/FeeVote_test.cpp index 360def1abf..62f1058de5 100644 --- a/src/test/app/FeeVote_test.cpp +++ b/src/test/app/FeeVote_test.cpp @@ -1,10 +1,9 @@ #include -#include #include #include -#include +#include #include #include #include @@ -114,7 +113,7 @@ bool applyFeeAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx) { auto const res = apply(env.app(), view, tx, ApplyFlags::tapNONE, env.journal); - return res.ter == tesSUCCESS; + return isTesSuccess(res.ter); } bool @@ -190,7 +189,7 @@ class FeeVote_test : public beast::unit_test::suite FeeSetup const defaultSetup; { // defaults - Section config; + Section const config; auto setup = setup_FeeVote(config); BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve); @@ -251,16 +250,17 @@ class FeeVote_test : public beast::unit_test::suite jtx::Env env(*this, jtx::testable_amendments() - featureXRPFees); auto ledger = std::make_shared( create_genesis, - env.app().config(), + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), std::vector{}, env.app().getNodeFamily()); // Create the next ledger to apply transaction to - ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); + ledger = std::make_shared(*ledger, env.app().getTimeKeeper().closeTime()); // Test successful fee transaction with legacy fields - FeeSettingsFields fields{ + FeeSettingsFields const fields{ .baseFee = 10, .reserveBase = 200000, .reserveIncrement = 50000, @@ -280,14 +280,15 @@ class FeeVote_test : public beast::unit_test::suite jtx::Env env(*this, jtx::testable_amendments() | featureXRPFees); auto ledger = std::make_shared( create_genesis, - env.app().config(), + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), std::vector{}, env.app().getNodeFamily()); // Create the next ledger to apply transaction to - ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); + ledger = std::make_shared(*ledger, env.app().getTimeKeeper().closeTime()); - FeeSettingsFields fields{ + FeeSettingsFields const fields{ .baseFeeDrops = XRPAmount{10}, .reserveBaseDrops = XRPAmount{200000}, .reserveIncrementDrops = XRPAmount{50000}}; @@ -312,12 +313,13 @@ class FeeVote_test : public beast::unit_test::suite jtx::Env env(*this, jtx::testable_amendments() - featureXRPFees); auto ledger = std::make_shared( create_genesis, - env.app().config(), + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), std::vector{}, env.app().getNodeFamily()); // Create the next ledger to apply transaction to - ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); + ledger = std::make_shared(*ledger, env.app().getTimeKeeper().closeTime()); // Test transaction with missing required legacy fields auto invalidTx = createInvalidFeeTx(ledger->rules(), ledger->seq(), true, false, 1); @@ -333,12 +335,13 @@ class FeeVote_test : public beast::unit_test::suite jtx::Env env(*this, jtx::testable_amendments() | featureXRPFees); auto ledger = std::make_shared( create_genesis, - env.app().config(), + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), std::vector{}, env.app().getNodeFamily()); // Create the next ledger to apply transaction to - ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); + ledger = std::make_shared(*ledger, env.app().getTimeKeeper().closeTime()); // Test transaction with missing required new fields auto invalidTx = createInvalidFeeTx(ledger->rules(), ledger->seq(), true, false, 3); @@ -358,10 +361,14 @@ class FeeVote_test : public beast::unit_test::suite jtx::Env env(*this, jtx::testable_amendments()); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); // Create the next ledger to apply transaction to - ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); + ledger = std::make_shared(*ledger, env.app().getTimeKeeper().closeTime()); auto feeTx = createFeeTx( ledger->rules(), @@ -393,11 +400,15 @@ class FeeVote_test : public beast::unit_test::suite jtx::Env env(*this, jtx::testable_amendments() | featureXRPFees); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); - ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); + ledger = std::make_shared(*ledger, env.app().getTimeKeeper().closeTime()); - FeeSettingsFields fields1{ + FeeSettingsFields const fields1{ .baseFeeDrops = XRPAmount{10}, .reserveBaseDrops = XRPAmount{200000}, .reserveIncrementDrops = XRPAmount{50000}}; @@ -412,9 +423,9 @@ class FeeVote_test : public beast::unit_test::suite BEAST_EXPECT(verifyFeeObject(ledger, ledger->rules(), fields1)); // Apply second fee transaction with different values - ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); + ledger = std::make_shared(*ledger, env.app().getTimeKeeper().closeTime()); - FeeSettingsFields fields2{ + FeeSettingsFields const fields2{ .baseFeeDrops = XRPAmount{20}, .reserveBaseDrops = XRPAmount{300000}, .reserveIncrementDrops = XRPAmount{75000}}; @@ -437,9 +448,13 @@ class FeeVote_test : public beast::unit_test::suite jtx::Env env(*this, jtx::testable_amendments() | featureXRPFees); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); - ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); + ledger = std::make_shared(*ledger, env.app().getTimeKeeper().closeTime()); // Test transaction with wrong ledger sequence auto feeTx = createFeeTx( @@ -464,11 +479,15 @@ class FeeVote_test : public beast::unit_test::suite jtx::Env env(*this, jtx::testable_amendments() | featureXRPFees); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); - ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); + ledger = std::make_shared(*ledger, env.app().getTimeKeeper().closeTime()); - FeeSettingsFields fields1{ + FeeSettingsFields const fields1{ .baseFeeDrops = XRPAmount{10}, .reserveBaseDrops = XRPAmount{200000}, .reserveIncrementDrops = XRPAmount{50000}}; @@ -482,10 +501,10 @@ class FeeVote_test : public beast::unit_test::suite BEAST_EXPECT(verifyFeeObject(ledger, ledger->rules(), fields1)); - ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); + ledger = std::make_shared(*ledger, env.app().getTimeKeeper().closeTime()); // Apply partial update (only some fields) - FeeSettingsFields fields2{ + FeeSettingsFields const fields2{ .baseFeeDrops = XRPAmount{20}, .reserveBaseDrops = XRPAmount{200000}}; auto feeTx2 = createFeeTx(ledger->rules(), ledger->seq(), fields2); @@ -506,9 +525,13 @@ class FeeVote_test : public beast::unit_test::suite jtx::Env env(*this, jtx::testable_amendments() | featureXRPFees); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); - ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); + ledger = std::make_shared(*ledger, env.app().getTimeKeeper().closeTime()); // Test invalid transaction with non-zero account - this should fail // validation @@ -540,11 +563,12 @@ class FeeVote_test : public beast::unit_test::suite // Test with XRPFees enabled { Env env(*this, testable_amendments() | featureXRPFees); - auto feeVote = make_FeeVote(setup, env.app().journal("FeeVote")); + auto feeVote = make_FeeVote(setup, env.app().getJournal("FeeVote")); auto ledger = std::make_shared( create_genesis, - env.app().config(), + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), std::vector{}, env.app().getNodeFamily()); @@ -552,7 +576,7 @@ class FeeVote_test : public beast::unit_test::suite auto pub = derivePublicKey(KeyType::secp256k1, sec); auto val = std::make_shared( - env.app().timeKeeper().now(), pub, sec, calcNodeID(pub), [](STValidation& v) { + env.app().getTimeKeeper().now(), pub, sec, calcNodeID(pub), [](STValidation& v) { v.setFieldU32(sfLedgerSequence, 12345); }); @@ -569,11 +593,12 @@ class FeeVote_test : public beast::unit_test::suite // Test with XRPFees disabled (legacy format) { Env env(*this, testable_amendments() - featureXRPFees); - auto feeVote = make_FeeVote(setup, env.app().journal("FeeVote")); + auto feeVote = make_FeeVote(setup, env.app().getJournal("FeeVote")); auto ledger = std::make_shared( create_genesis, - env.app().config(), + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), std::vector{}, env.app().getNodeFamily()); @@ -581,7 +606,7 @@ class FeeVote_test : public beast::unit_test::suite auto pub = derivePublicKey(KeyType::secp256k1, sec); auto val = std::make_shared( - env.app().timeKeeper().now(), pub, sec, calcNodeID(pub), [](STValidation& v) { + env.app().getTimeKeeper().now(), pub, sec, calcNodeID(pub), [](STValidation& v) { v.setFieldU32(sfLedgerSequence, 12345); }); @@ -614,15 +639,19 @@ class FeeVote_test : public beast::unit_test::suite BEAST_EXPECT(env.current()->fees().reserve == XRPAmount{200'000'000}); BEAST_EXPECT(env.current()->fees().increment == XRPAmount{50'000'000}); - auto feeVote = make_FeeVote(setup, env.app().journal("FeeVote")); + auto feeVote = make_FeeVote(setup, env.app().getJournal("FeeVote")); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); // doVoting requires a flag ledger (every 256th ledger) // We need to create a ledger at sequence 256 to make it a flag ledger for (int i = 0; i < 256 - 1; ++i) { - ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); + ledger = std::make_shared(*ledger, env.app().getTimeKeeper().closeTime()); } BEAST_EXPECT(ledger->isFlagLedger()); @@ -635,14 +664,14 @@ class FeeVote_test : public beast::unit_test::suite auto pub = derivePublicKey(KeyType::secp256k1, sec); auto val = std::make_shared( - env.app().timeKeeper().now(), pub, sec, calcNodeID(pub), [&](STValidation& v) { + 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}); }); - if (i % 2) + if ((i % 2) != 0) val->setTrusted(); validations.push_back(val); } diff --git a/src/test/app/FixNFTokenPageLinks_test.cpp b/src/test/app/FixNFTokenPageLinks_test.cpp index 8022c52a5d..e5ecdf2639 100644 --- a/src/test/app/FixNFTokenPageLinks_test.cpp +++ b/src/test/app/FixNFTokenPageLinks_test.cpp @@ -1,9 +1,9 @@ #include +#include #include #include #include -#include namespace xrpl { @@ -62,7 +62,7 @@ class FixNFTokenPageLinks_test : public beast::unit_test::suite // 0, 3, 2, 5, 4, 7... // in sets of 16 NFTs we can get each page to be fully // populated. - std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0); + std::uint32_t const intTaxon = (i / 16) + (((i & 0b10000) != 0u) ? 2 : 0); uint32_t const extTaxon = internalTaxon(owner, intTaxon); nfts.push_back(token::getNextID(env, owner, extTaxon, tfTransferable)); env(token::mint(owner, extTaxon), txflags(tfTransferable)); diff --git a/src/test/app/Flow_test.cpp b/src/test/app/Flow_test.cpp index c56cef39f8..0bc5bd1727 100644 --- a/src/test/app/Flow_test.cpp +++ b/src/test/app/Flow_test.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -196,6 +197,7 @@ struct Flow_test : public beast::unit_test::suite // Dan -> Bob -> Alice -> Carol; vary bobDanQIn and bobAliceQOut for (auto bobDanQIn : {80, 100, 120}) + { for (auto bobAliceQOut : {80, 100, 120}) { Env env(*this, features); @@ -213,12 +215,17 @@ struct Flow_test : public beast::unit_test::suite txflags(tfNoRippleDirect)); env.require(balance(bob, USDA(90))); if (bobAliceQOut > bobDanQIn) + { env.require( balance(bob, USDD(10.0 * double(bobAliceQOut) / double(bobDanQIn)))); + } else + { env.require(balance(bob, USDD(10))); + } env.require(balance(carol, USDA(10))); } + } // bob -> alice -> carol; vary carolAliceQIn for (auto carolAliceQIn : {80, 100, 120}) @@ -234,7 +241,7 @@ struct Flow_test : public beast::unit_test::suite env.require(balance(bob, USDA(10))); env(pay(bob, carol, USDA(5)), sendmax(USDA(10))); auto const effectiveQ = carolAliceQIn > 100 ? 1.0 : carolAliceQIn / 100.0; - env.require(balance(bob, USDA(10.0 - 5.0 / effectiveQ))); + env.require(balance(bob, USDA(10.0 - (5.0 / effectiveQ)))); } // bob -> alice -> carol; bobAliceQOut varies. @@ -428,9 +435,9 @@ struct Flow_test : public beast::unit_test::suite BEAST_EXPECT(isOffer(env, bob, BTC(60), EUR(50))); BEAST_EXPECT(isOffer(env, carol, BTC(1000), EUR(1))); - auto flowJournal = env.app().logs().journal("Flow"); + auto flowJournal = env.app().getJournal("Flow"); auto const flowResult = [&] { - STAmount deliver(USD(51)); + STAmount const deliver(USD(51)); STAmount smax(BTC(61)); PaymentSandbox sb(env.current().get(), tapNONE); STPathSet paths; @@ -443,10 +450,10 @@ struct Flow_test : public beast::unit_test::suite }; { // BTC -> USD - STPath p1({IPE(USD.issue())}); + STPath const p1({IPE(USD.issue())}); paths.push_back(p1); // BTC -> EUR -> USD - STPath p2({IPE(EUR.issue()), IPE(USD.issue())}); + STPath const p2({IPE(EUR.issue()), IPE(USD.issue())}); paths.push_back(p2); } @@ -467,13 +474,15 @@ struct Flow_test : public beast::unit_test::suite }(); BEAST_EXPECT(flowResult.removableOffers.size() == 1); - env.app().openLedger().modify([&](OpenView& view, beast::Journal j) { + env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) { if (flowResult.removableOffers.empty()) return false; Sandbox sb(&view, tapNONE); for (auto const& o : flowResult.removableOffers) + { if (auto ok = sb.peek(keylet::offer(o))) offerDelete(sb, ok, flowJournal); + } sb.apply(view); return true; }); @@ -867,8 +876,10 @@ struct Flow_test : public beast::unit_test::suite env.close(); env(trust(bob, USD(20))); - STAmount tinyAmt1{USD.issue(), 9000000000000000ll, -17, false, STAmount::unchecked{}}; - STAmount tinyAmt3{USD.issue(), 9000000000000003ll, -17, false, STAmount::unchecked{}}; + STAmount const tinyAmt1{ + USD.issue(), 9000000000000000ll, -17, false, STAmount::unchecked{}}; + STAmount const tinyAmt3{ + USD.issue(), 9000000000000003ll, -17, false, STAmount::unchecked{}}; env(offer(gw, drops(9000000000), tinyAmt3)); env(pay(alice, bob, tinyAmt1), @@ -891,8 +902,10 @@ struct Flow_test : public beast::unit_test::suite env.close(); env(trust(alice, USD(20))); - STAmount tinyAmt1{USD.issue(), 9000000000000000ll, -17, false, STAmount::unchecked{}}; - STAmount tinyAmt3{USD.issue(), 9000000000000003ll, -17, false, STAmount::unchecked{}}; + STAmount const tinyAmt1{ + USD.issue(), 9000000000000000ll, -17, false, STAmount::unchecked{}}; + STAmount const tinyAmt3{ + USD.issue(), 9000000000000003ll, -17, false, STAmount::unchecked{}}; env(pay(gw, alice, tinyAmt1)); diff --git a/src/test/app/Freeze_test.cpp b/src/test/app/Freeze_test.cpp index 6f89163c04..ed5ee47578 100644 --- a/src/test/app/Freeze_test.cpp +++ b/src/test/app/Freeze_test.cpp @@ -19,9 +19,9 @@ class Freeze_test : public beast::unit_test::suite using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account alice{"alice"}; - Account bob{"bob"}; + Account const G1{"G1"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(1000), G1, alice, bob); env.close(); @@ -168,8 +168,8 @@ class Freeze_test : public beast::unit_test::suite using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; + Account const G1{"G1"}; + Account const A1{"A1"}; env.fund(XRP(10000), G1, A1); env.close(); @@ -259,8 +259,8 @@ class Freeze_test : public beast::unit_test::suite using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; + Account const G1{"G1"}; + Account const A1{"A1"}; env.fund(XRP(10000), G1, A1); env.close(); @@ -308,8 +308,8 @@ class Freeze_test : public beast::unit_test::suite using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; + Account const G1{"G1"}; + Account const A1{"A1"}; env.fund(XRP(10000), G1, A1); env.close(); @@ -347,11 +347,11 @@ class Freeze_test : public beast::unit_test::suite using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; - Account A2{"A2"}; - Account A3{"A3"}; - Account A4{"A4"}; + Account const G1{"G1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const A3{"A3"}; + Account const A4{"A4"}; env.fund(XRP(12000), G1); env.fund(XRP(1000), A1); @@ -497,10 +497,10 @@ class Freeze_test : public beast::unit_test::suite using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; - Account frozenAcc{"A2"}; - Account deepFrozenAcc{"A3"}; + Account const G1{"G1"}; + Account const A1{"A1"}; + Account const frozenAcc{"A2"}; + Account const deepFrozenAcc{"A3"}; env.fund(XRP(12000), G1); env.fund(XRP(1000), A1); @@ -608,10 +608,10 @@ class Freeze_test : public beast::unit_test::suite using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A2{"A2"}; - Account A3{"A3"}; - Account A4{"A4"}; + Account const G1{"G1"}; + Account const A2{"A2"}; + Account const A3{"A3"}; + Account const A4{"A4"}; env.fund(XRP(1000), G1, A3, A4); env.fund(XRP(2000), A2); @@ -705,10 +705,10 @@ class Freeze_test : public beast::unit_test::suite using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; - Account A2{"A2"}; - Account A3{"A3"}; + Account const G1{"G1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const A3{"A3"}; auto const USD{G1["USD"]}; env.fund(XRP(10000), G1, A1, A2, A3); @@ -935,9 +935,9 @@ class Freeze_test : public beast::unit_test::suite using path = test::jtx::path; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; - Account A2{"A2"}; + Account const G1{"G1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; auto const USD{G1["USD"]}; env.fund(XRP(10000), G1, A1, A2); @@ -1161,9 +1161,9 @@ class Freeze_test : public beast::unit_test::suite using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; - Account A2{"A2"}; + Account const G1{"G1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; auto const USD{G1["USD"]}; env.fund(XRP(10000), G1, A1, A2); @@ -1283,9 +1283,9 @@ class Freeze_test : public beast::unit_test::suite using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; - Account A2{"A2"}; + Account const G1{"G1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; auto const USD{G1["USD"]}; env.fund(XRP(10000), G1, A1, A2); @@ -1578,9 +1578,9 @@ class Freeze_test : public beast::unit_test::suite using path = test::jtx::path; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; - Account A2{"A2"}; + Account const G1{"G1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; auto const USD{G1["USD"]}; env.fund(XRP(10000), G1, A1, A2); @@ -1593,7 +1593,7 @@ class Freeze_test : public beast::unit_test::suite env(pay(G1, A2, USD(1000))); env.close(); - AMM ammG1(env, G1, XRP(1'000), USD(1'000)); + AMM const ammG1(env, G1, XRP(1'000), USD(1'000)); env.close(); // Testing basic payment using AMM when freezing one of the trust lines. @@ -1668,9 +1668,9 @@ class Freeze_test : public beast::unit_test::suite using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; - Account A2{"A2"}; + Account const G1{"G1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; auto const USD{G1["USD"]}; env.fund(XRP(10000), G1, A1, A2); @@ -1829,7 +1829,7 @@ class Freeze_test : public beast::unit_test::suite // Testing brokered offer acceptance if (features[featureDeepFreeze] && features[fixEnforceNFTokenTrustlineV2]) { - Account broker{"broker"}; + Account const broker{"broker"}; env.fund(XRP(10000), broker); env.close(); env(trust(G1, broker["USD"](1000), tfSetFreeze | tfSetDeepFreeze)); @@ -1855,7 +1855,7 @@ class Freeze_test : public beast::unit_test::suite // Testing transfer fee if (features[featureDeepFreeze] && features[fixEnforceNFTokenTrustlineV2]) { - Account minter{"minter"}; + Account const minter{"minter"}; env.fund(XRP(10000), minter); env.close(); env(trust(G1, minter["USD"](1000))); @@ -1907,13 +1907,13 @@ class Freeze_test : public beast::unit_test::suite } // Helper function that returns the index of the next check on account - uint256 + static uint256 getCheckIndex(AccountID const& account, std::uint32_t uSequence) { return keylet::check(account, uSequence).key; } - uint256 + static uint256 createNFTSellOffer( test::jtx::Env& env, test::jtx::Account const& account, diff --git a/src/test/app/HashRouter_test.cpp b/src/test/app/HashRouter_test.cpp index 753922a67d..e53515e421 100644 --- a/src/test/app/HashRouter_test.cpp +++ b/src/test/app/HashRouter_test.cpp @@ -10,7 +10,7 @@ namespace test { class HashRouter_test : public beast::unit_test::suite { - HashRouter::Setup + static HashRouter::Setup getSetup(std::chrono::seconds hold, std::chrono::seconds relay) { HashRouter::Setup setup; @@ -27,9 +27,9 @@ class HashRouter_test : public beast::unit_test::suite TestStopwatch stopwatch; HashRouter router(getSetup(2s, 1s), stopwatch); - HashRouterFlags key1(HashRouterFlags::PRIVATE1); - HashRouterFlags key2(HashRouterFlags::PRIVATE2); - HashRouterFlags key3(HashRouterFlags::PRIVATE3); + HashRouterFlags const key1(HashRouterFlags::PRIVATE1); + HashRouterFlags const key2(HashRouterFlags::PRIVATE2); + HashRouterFlags const key3(HashRouterFlags::PRIVATE3); auto const ukey1 = uint256{static_cast(key1)}; auto const ukey2 = uint256{static_cast(key2)}; @@ -69,10 +69,10 @@ class HashRouter_test : public beast::unit_test::suite TestStopwatch stopwatch; HashRouter router(getSetup(2s, 1s), stopwatch); - HashRouterFlags key1(HashRouterFlags::PRIVATE1); - HashRouterFlags key2(HashRouterFlags::PRIVATE2); - HashRouterFlags key3(HashRouterFlags::PRIVATE3); - HashRouterFlags key4(HashRouterFlags::PRIVATE4); + HashRouterFlags const key1(HashRouterFlags::PRIVATE1); + HashRouterFlags const key2(HashRouterFlags::PRIVATE2); + HashRouterFlags const key3(HashRouterFlags::PRIVATE3); + HashRouterFlags const key4(HashRouterFlags::PRIVATE4); auto const ukey1 = uint256{static_cast(key1)}; auto const ukey2 = uint256{static_cast(key2)}; @@ -231,7 +231,7 @@ class HashRouter_test : public beast::unit_test::suite ++stopwatch; // Confirm that peers list is empty. peers = router.shouldRelay(key1); - BEAST_EXPECT(peers && peers->size() == 0); + BEAST_EXPECT(peers && peers->empty()); } void @@ -242,8 +242,8 @@ class HashRouter_test : public beast::unit_test::suite TestStopwatch stopwatch; HashRouter router(getSetup(5s, 1s), stopwatch); uint256 const key(1); - HashRouter::PeerShortID peer = 1; - HashRouterFlags flags; + HashRouter::PeerShortID const peer = 1; + HashRouterFlags flags = HashRouterFlags::UNDEFINED; BEAST_EXPECT(router.shouldProcess(key, peer, flags, 1s)); BEAST_EXPECT(!router.shouldProcess(key, peer, flags, 1s)); @@ -259,7 +259,7 @@ class HashRouter_test : public beast::unit_test::suite using namespace std::chrono_literals; { - Config cfg; + Config const cfg; // default auto const setup = setup_HashRouter(cfg); BEAST_EXPECT(setup.holdTime == 300s); @@ -298,7 +298,7 @@ class HashRouter_test : public beast::unit_test::suite } catch (std::exception const& e) { - std::string expected = + std::string const expected = "HashRouter relay time must be less than or equal to hold " "time"; BEAST_EXPECT(e.what() == expected); @@ -317,7 +317,7 @@ class HashRouter_test : public beast::unit_test::suite } catch (std::exception const& e) { - std::string expected = + std::string const expected = "HashRouter hold time must be at least 12 seconds (the " "approximate validation time for three " "ledgers)."; @@ -337,7 +337,7 @@ class HashRouter_test : public beast::unit_test::suite } catch (std::exception const& e) { - std::string expected = + std::string const expected = "HashRouter relay time must be at least 8 seconds (the " "approximate validation time for two ledgers)."; BEAST_EXPECT(e.what() == expected); @@ -365,9 +365,9 @@ class HashRouter_test : public beast::unit_test::suite using HF = HashRouterFlags; using UHF = std::underlying_type_t; - HF f1 = HF::BAD; - HF f2 = HF::SAVED; - HF combined = f1 | f2; + HF const f1 = HF::BAD; + HF const f2 = HF::SAVED; + HF const combined = f1 | f2; BEAST_EXPECT(static_cast(combined) == (static_cast(f1) | static_cast(f2))); @@ -375,7 +375,7 @@ class HashRouter_test : public beast::unit_test::suite temp |= f2; BEAST_EXPECT(temp == combined); - HF intersect = combined & f1; + HF const intersect = combined & f1; BEAST_EXPECT(intersect == f1); HF temp2 = combined; diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 6a010a7e82..72ed7ba57b 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -4,6 +4,9 @@ #include #include +#include +#include +#include #include #include #include @@ -71,7 +74,7 @@ class Invariants_test : public beast::unit_test::suite Preclose const& preclose = {}, TxAccount setTxAccount = TxAccount::None) { - return doInvariantCheck( + doInvariantCheck( test::jtx::Env(*this, defaultAmendments()), expect_logs, precheck, @@ -105,11 +108,12 @@ class Invariants_test : public beast::unit_test::suite if (setTxAccount != TxAccount::None) tx.setAccountID(sfAccount, setTxAccount == TxAccount::A1 ? A1.id() : A2.id()); - return doInvariantCheck(std::move(env), A1, A2, expect_logs, precheck, fee, tx, ters); + doInvariantCheck(std::move(env), A1, A2, expect_logs, precheck, fee, tx, ters); } void doInvariantCheck( + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) test::jtx::Env&& env, test::jtx::Account const& A1, test::jtx::Account const& A2, @@ -123,7 +127,7 @@ class Invariants_test : public beast::unit_test::suite OpenView ov{*env.current()}; test::StreamSink sink{beast::severities::kWarning}; - beast::Journal jlog{sink}; + beast::Journal const jlog{sink}; ApplyContext ac{env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, tapNONE, jlog}; BEAST_EXPECT(precheck(A1, A2, ac)); @@ -139,7 +143,7 @@ class Invariants_test : public beast::unit_test::suite BEAST_EXPECTS(terExpect == terActual, std::to_string(TERtoInt(terActual))); auto const messages = sink.messages().str(); - if (terActual != tesSUCCESS) + if (!isTesSuccess(terActual)) { BEAST_EXPECTS( messages.starts_with("Invariant failed:") || @@ -394,22 +398,21 @@ class Invariants_test : public beast::unit_test::suite for (auto const& trustKeylet : {keylet::line(ammAcctID, A1["USD"]), keylet::line(A1, ammIssue)}) { - if (auto const line = ac.view().peek(trustKeylet); !line) + auto const line = ac.view().peek(trustKeylet); + if (!line) { return false; } - else - { - STAmount const lowLimit = line->at(sfLowLimit); - STAmount const highLimit = line->at(sfHighLimit); - BEAST_EXPECT( - trustDelete( - ac.view(), - line, - lowLimit.getIssuer(), - highLimit.getIssuer(), - ac.journal) == tesSUCCESS); - } + + STAmount const lowLimit = line->at(sfLowLimit); + STAmount const highLimit = line->at(sfHighLimit); + BEAST_EXPECT( + trustDelete( + ac.view(), + line, + lowLimit.getIssuer(), + highLimit.getIssuer(), + ac.journal) == tesSUCCESS); } auto const ammSle = ac.view().peek(keylet::amm(ammKey)); @@ -842,7 +845,7 @@ class Invariants_test : public beast::unit_test::suite auto sleNew = std::make_shared(keylet::escrow(A1, (*sle)[sfSequence] + 2)); Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)}; - STAmount amt(usd, -1); + STAmount const amt(usd, -1); sleNew->setFieldAmount(sfAmount, amt); ac.view().insert(sleNew); return true; @@ -859,7 +862,7 @@ class Invariants_test : public beast::unit_test::suite auto sleNew = std::make_shared(keylet::escrow(A1, (*sle)[sfSequence] + 2)); Issue const bad{badCurrency(), AccountID(0x4985601)}; - STAmount amt(bad, 1); + STAmount const amt(bad, 1); sleNew->setFieldAmount(sfAmount, amt); ac.view().insert(sleNew); return true; @@ -876,7 +879,7 @@ class Invariants_test : public beast::unit_test::suite auto sleNew = std::make_shared(keylet::escrow(A1, (*sle)[sfSequence] + 2)); MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}}; - STAmount amt(mpt, -1); + STAmount const amt(mpt, -1); sleNew->setFieldAmount(sfAmount, amt); ac.view().insert(sleNew); return true; @@ -1023,8 +1026,7 @@ class Invariants_test : public beast::unit_test::suite auto const sleNew = std::make_shared(acctKeylet); sleNew->setFieldU32(sfSequence, 0); sleNew->setFieldH256(sfAMMID, uint256(1)); - sleNew->setFieldU32( - sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDefaultRipple); + sleNew->setFieldU32(sfFlags, lsfDisableMaster | lsfDefaultRipple); ac.view().insert(sleNew); return true; }, @@ -1190,7 +1192,7 @@ class Invariants_test : public beast::unit_test::suite STArray nfTokens = makeNFTokenIDs(1); auto nftPage = std::make_shared(keylet::nftpage( keylet::nftpage_max(A1), ++(nfTokens[0].getFieldH256(sfNFTokenID)))); - nftPage->setFieldArray(sfNFTokens, std::move(nfTokens)); + nftPage->setFieldArray(sfNFTokens, nfTokens); nftPage->setFieldH256(sfNextPageMin, keylet::nftpage_max(A2).key); ac.view().insert(nftPage); @@ -1203,7 +1205,7 @@ class Invariants_test : public beast::unit_test::suite STArray nfTokens = makeNFTokenIDs(2); auto nftPage = std::make_shared(keylet::nftpage( keylet::nftpage_max(A1), (nfTokens[1].getFieldH256(sfNFTokenID)))); - nftPage->setFieldArray(sfNFTokens, std::move(nfTokens)); + nftPage->setFieldArray(sfNFTokens, nfTokens); ac.view().insert(nftPage); return true; @@ -1224,7 +1226,7 @@ class Invariants_test : public beast::unit_test::suite sle->setAccountID(sfOwner, A1); sle->setFieldU32(sfSequence, seq); - if (numCreds) + if (numCreds != 0u) { // This array is sorted naturally, but if you willing to change this // behavior don't forget to use credentials::makeSorted @@ -1250,8 +1252,8 @@ class Invariants_test : public beast::unit_test::suite using namespace test::jtx; bool const fixPDEnabled = features[fixPermissionedDomainInvariant]; - std::initializer_list badTers = {tecINVARIANT_FAILED, tecINVARIANT_FAILED}; - std::initializer_list failTers = {tecINVARIANT_FAILED, tefINVARIANT_FAILED}; + std::initializer_list const badTers = {tecINVARIANT_FAILED, tecINVARIANT_FAILED}; + std::initializer_list const failTers = {tecINVARIANT_FAILED, tefINVARIANT_FAILED}; testcase << "PermissionedDomain" + std::string(fixPDEnabled ? " fix" : ""); @@ -1335,7 +1337,7 @@ class Invariants_test : public beast::unit_test::suite // update PD with empty rules { - STArray credentials(sfAcceptedCredentials, 2); + STArray const credentials(sfAcceptedCredentials, 2); slePd->setFieldArray(sfAcceptedCredentials, credentials); ac.view().update(slePd); } @@ -1435,15 +1437,16 @@ class Invariants_test : public beast::unit_test::suite STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}}, fixPDEnabled ? failTers : badTers); - std::initializer_list goodTers = {tesSUCCESS, tesSUCCESS}; + std::initializer_list const goodTers = {tesSUCCESS, tesSUCCESS}; - std::vector badMoreThan1{ + std::vector const badMoreThan1{ {"transaction affected more than 1 permissioned domain entry."}}; - std::vector emptyV; - std::vector badNoDomains{{"no domain objects affected by"}}; - std::vector badNotDeleted{{"domain object modified, but not deleted by "}}; - std::vector badDeleted{{"domain object deleted by"}}; - std::vector badTx{ + std::vector const emptyV; + std::vector const badNoDomains{{"no domain objects affected by"}}; + std::vector const badNotDeleted{ + {"domain object modified, but not deleted by "}}; + std::vector const badDeleted{{"domain object deleted by"}}; + std::vector const badTx{ {"domain object(s) affected by an unauthorized transaction."}}; { @@ -1593,11 +1596,11 @@ class Invariants_test : public beast::unit_test::suite using namespace jtx; AccountID pseudoAccountID; - Preclose createPseudo = [&, this](Account const& a, Account const& b, Env& env) { + Preclose const createPseudo = [&, this](Account const& a, Account const& b, Env& env) { PrettyAsset const xrpAsset{xrpIssue(), 1'000'000}; // Create vault - Vault vault{env}; + Vault const vault{env}; auto [tx, vKeylet] = vault.create({.owner = a, .asset = xrpAsset}); env(tx); env.close(); @@ -1721,7 +1724,7 @@ class Invariants_test : public beast::unit_test::suite std::uint32_t const seq = env.seq(A1); env(pdomain::setTx(A1, credentials)); - uint256 key = pdomain::getNewDomain(env.meta()); + uint256 const key = pdomain::getNewDomain(env.meta()); // std::cout << "PD, acc: " << A1.id() << ", seq: " << seq << ", k: " << // key << std::endl; @@ -1939,7 +1942,7 @@ class Invariants_test : public beast::unit_test::suite // Create vault uint256 vaultID; - Vault vault{env}; + Vault const vault{env}; auto [tx, vKeylet] = vault.create({.owner = a, .asset = asset}); env(tx); BEAST_EXPECT(env.le(vKeylet)); @@ -1964,7 +1967,7 @@ class Invariants_test : public beast::unit_test::suite // Initialize with a placeholder value because there's no default ctor Keylet loanBrokerKeylet = keylet::amendments(); - Preclose createLoanBroker = [&, this](Account const& a, Account const& b, Env& env) { + Preclose const createLoanBroker = [&, this](Account const& a, Account const& b, Env& env) { PrettyAsset const xrpAsset{xrpIssue(), 1'000'000}; loanBrokerKeylet = this->createLoanBroker(a, env, xrpAsset); @@ -2043,40 +2046,40 @@ class Invariants_test : public beast::unit_test::suite { // Initialize with a placeholder value because there's no default // ctor - Keylet loanBrokerKeylet = keylet::amendments(); - Preclose createLoanBroker = [&, this]( - Account const& alice, Account const& issuer, Env& env) { - PrettyAsset const asset = [&]() { - switch (assetType) - { - case Asset::IOU: { - PrettyAsset const iouAsset = issuer["IOU"]; - env(trust(alice, iouAsset(1000))); - env(pay(issuer, alice, iouAsset(1000))); - env.close(); - return iouAsset; - } - - case Asset::MPT: { - MPTTester mptt{env, issuer, mptInitNoFund}; - mptt.create( - {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); - PrettyAsset const mptAsset = mptt.issuanceID(); - mptt.authorize({.account = alice}); - env(pay(issuer, alice, mptAsset(1000))); - env.close(); - return mptAsset; - } - - case Asset::XRP: - default: - return PrettyAsset{xrpIssue(), 1'000'000}; + auto const setupAsset = + [&](Account const& alice, Account const& issuer, Env& env) -> PrettyAsset { + switch (assetType) + { + case Asset::IOU: { + PrettyAsset const iouAsset = issuer["IOU"]; + env(trust(alice, iouAsset(1000))); + env(pay(issuer, alice, iouAsset(1000))); + env.close(); + return iouAsset; } - }(); - loanBrokerKeylet = this->createLoanBroker(alice, env, asset); - return BEAST_EXPECT(env.le(loanBrokerKeylet)); + case Asset::MPT: { + MPTTester mptt{env, issuer, mptInitNoFund}; + mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); + PrettyAsset const mptAsset = mptt.issuanceID(); + mptt.authorize({.account = alice}); + env(pay(issuer, alice, mptAsset(1000))); + env.close(); + return mptAsset; + } + case Asset::XRP: + default: + return PrettyAsset{xrpIssue(), 1'000'000}; + } }; + Keylet loanBrokerKeylet = keylet::amendments(); + Preclose const createLoanBroker = + [&, this](Account const& alice, Account const& issuer, Env& env) { + auto const asset = setupAsset(alice, issuer, env); + loanBrokerKeylet = this->createLoanBroker(alice, env, asset); + return BEAST_EXPECT(env.le(loanBrokerKeylet)); + }; + // Ensure the test scenarios are set up completely. The test cases // will need to recompute any of these values it needs for itself // rather than trying to return a bunch of items @@ -2246,6 +2249,56 @@ class Invariants_test : public beast::unit_test::suite STTx{ttLOAN_BROKER_SET, [](STObject& tx) {}}, {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, createLoanBroker); + + // Test: cover available less than pseudo-account asset balance + { + Keylet brokerKeylet = keylet::amendments(); + Preclose const createBrokerWithCover = + [&, this](Account const& alice, Account const& issuer, Env& env) { + auto const asset = setupAsset(alice, issuer, env); + brokerKeylet = this->createLoanBroker(alice, env, asset); + if (!BEAST_EXPECT(env.le(brokerKeylet))) + return false; + env(loanBroker::coverDeposit(alice, brokerKeylet.key, asset(10))); + env.close(); + return BEAST_EXPECT(env.le(brokerKeylet)); + }; + + doInvariantCheck( + {{"Loan Broker cover available is less than pseudo-account asset balance"}}, + [&](Account const&, Account const&, ApplyContext& ac) { + auto sle = ac.view().peek(brokerKeylet); + if (!BEAST_EXPECT(sle)) + return false; + // Pseudo-account holds 10 units, set cover to 5 + sle->at(sfCoverAvailable) = Number(5); + ac.view().update(sle); + return true; + }, + XRPAmount{}, + STTx{ttLOAN_BROKER_SET, [](STObject& tx) {}}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + createBrokerWithCover); + } + + // Test: cover available greater than pseudo-account asset balance + // (requires fixSecurity3_1_3) + doInvariantCheck( + {{"Loan Broker cover available is greater than pseudo-account asset balance"}}, + [&](Account const&, Account const&, ApplyContext& ac) { + auto sle = ac.view().peek(loanBrokerKeylet); + if (!BEAST_EXPECT(sle)) + return false; + // Pseudo-account has no cover deposited; set cover + // higher than any incidental balance + sle->at(sfCoverAvailable) = Number(1'000'000); + ac.view().update(sle); + return true; + }, + XRPAmount{}, + STTx{ttLOAN_BROKER_SET, [](STObject& tx) {}}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + createLoanBroker); } } @@ -2261,14 +2314,16 @@ class Invariants_test : public beast::unit_test::suite }; struct Adjustments { - std::optional assetsTotal = {}; - std::optional assetsAvailable = {}; - std::optional lossUnrealized = {}; - std::optional assetsMaximum = {}; - std::optional sharesTotal = {}; - std::optional vaultAssets = {}; - std::optional accountAssets = {}; - std::optional accountShares = {}; + // NOLINTBEGIN(readability-redundant-member-init) + std::optional assetsTotal = std::nullopt; + std::optional assetsAvailable = std::nullopt; + std::optional lossUnrealized = std::nullopt; + std::optional assetsMaximum = std::nullopt; + std::optional sharesTotal = std::nullopt; + std::optional vaultAssets = std::nullopt; + std::optional accountAssets = std::nullopt; + std::optional accountShares = std::nullopt; + // NOLINTEND(readability-redundant-member-init) }; auto constexpr adjust = [&](ApplyView& ac, xrpl::Keylet keylet, Adjustments args) { auto sleVault = ac.peek(keylet); @@ -2290,8 +2345,10 @@ class Invariants_test : public beast::unit_test::suite if (args.assetsTotal) (*sleVault)[sfAssetsTotal] = *(*sleVault)[sfAssetsTotal] + *args.assetsTotal; if (args.assetsAvailable) + { (*sleVault)[sfAssetsAvailable] = *(*sleVault)[sfAssetsAvailable] + *args.assetsAvailable; + } ac.update(sleVault); if (args.sharesTotal) @@ -2324,7 +2381,9 @@ class Invariants_test : public beast::unit_test::suite ac.update(sleMPToken); } else + { return false; // Not supporting testing with IOU + } } if (args.accountAssets) @@ -2348,7 +2407,9 @@ class Invariants_test : public beast::unit_test::suite ac.update(sleMPToken); } else + { return false; // Not supporting testing with IOU + } } if (args.accountShares) @@ -2382,7 +2443,7 @@ class Invariants_test : public beast::unit_test::suite Account A4{"A4"}; auto const precloseXrp = [&](Account const& A1, Account const& A2, Env& env) -> bool { env.fund(XRP(1000), A3, A4); - Vault vault{env}; + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); env(vault.deposit({.depositor = A1, .id = keylet.key, .amount = XRP(10)})); @@ -2406,7 +2467,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_DELETE, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, _] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -2426,7 +2487,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttPAYMENT, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, _] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -2446,7 +2507,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttPAYMENT, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, _] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -2482,7 +2543,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_SET, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, _] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -2511,7 +2572,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_DELETE, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; { auto [tx, _] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); @@ -2557,7 +2618,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_DELETE, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, _] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -2583,7 +2644,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_DELETE, [](STObject&) {}}, {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); env(vault.deposit({.depositor = A1, .id = keylet.key, .amount = XRP(10)})); @@ -2619,7 +2680,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_CREATE, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, _] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -2632,7 +2693,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_DEPOSIT, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, _] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -2645,7 +2706,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_WITHDRAW, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, _] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -2658,7 +2719,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_CLAWBACK, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, _] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -2671,7 +2732,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_DELETE, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, _] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -2697,7 +2758,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_SET, [](STObject&) {}}, {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, _] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -2719,7 +2780,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_WITHDRAW, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); env(vault.deposit({.depositor = A1, .id = keylet.key, .amount = XRP(10)})); @@ -2959,7 +3020,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_CREATE, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -2985,7 +3046,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_CREATE, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -3012,7 +3073,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_CREATE, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -3040,7 +3101,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_CREATE, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -3064,7 +3125,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_CREATE, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -3092,7 +3153,7 @@ class Invariants_test : public beast::unit_test::suite STTx{ttVAULT_CREATE, [](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, [&](Account const& A1, Account const& A2, Env& env) { - Vault vault{env}; + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = A1, .asset = xrpIssue()}); env(tx); return true; @@ -3647,7 +3708,7 @@ class Invariants_test : public beast::unit_test::suite } auto const mptID = makeMptID(env.seq(A3) - 1, A3); - Asset asset = MPTIssue(mptID); + Asset const asset = MPTIssue(mptID); // Authorize A1 A2 A4 { Json::Value jv; @@ -3670,7 +3731,7 @@ class Invariants_test : public beast::unit_test::suite env.close(); } - Vault vault{env}; + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = A1, .asset = asset}); env(tx); env(vault.deposit({.depositor = A1, .id = keylet.key, .amount = asset(10)})); diff --git a/src/test/app/LPTokenTransfer_test.cpp b/src/test/app/LPTokenTransfer_test.cpp index 15a8837c4f..265ff7d2ef 100644 --- a/src/test/app/LPTokenTransfer_test.cpp +++ b/src/test/app/LPTokenTransfer_test.cpp @@ -299,9 +299,13 @@ class LPTokenTransfer_test : public jtx::AMMTest // with fixFrozenLPTokenTransfer enabled, bob fails to cash the check if (features[fixFrozenLPTokenTransfer]) + { env(check::cash(bob, carolChkId, STAmount{lpIssue, 10}), ter(tecPATH_PARTIAL)); + } else + { env(check::cash(bob, carolChkId, STAmount{lpIssue, 10})); + } env.close(); diff --git a/src/test/app/LedgerHistory_test.cpp b/src/test/app/LedgerHistory_test.cpp index 93ab4a6210..453c424251 100644 --- a/src/test/app/LedgerHistory_test.cpp +++ b/src/test/app/LedgerHistory_test.cpp @@ -38,7 +38,8 @@ public: assert(!stx); return std::make_shared( create_genesis, - env.app().config(), + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), std::vector{}, env.app().getNodeFamily()); } @@ -137,17 +138,17 @@ public: Env env{*this, envconfig(), std::make_unique(msg, &found)}; LedgerHistory lh{beast::insight::NullCollector::New(), env.app()}; - Account alice{"A1"}; - Account bob{"A2"}; + Account const alice{"A1"}; + Account const bob{"A2"}; env.fund(XRP(1000), alice, bob); env.close(); auto const ledgerBase = env.app().getLedgerMaster().getClosedLedger(); - JTx txAlice = env.jt(noop(alice)); + JTx const txAlice = env.jt(noop(alice)); auto const ledgerA = makeLedger(ledgerBase, env, lh, 4s, txAlice.stx); - JTx txBob = env.jt(noop(bob)); + JTx const txBob = env.jt(noop(bob)); auto const ledgerB = makeLedger(ledgerBase, env, lh, 4s, txBob.stx); lh.builtLedger(ledgerA, txAlice.stx->getTransactionID(), {}); diff --git a/src/test/app/LedgerLoad_test.cpp b/src/test/app/LedgerLoad_test.cpp index 0a1ffc024a..1df41671fe 100644 --- a/src/test/app/LedgerLoad_test.cpp +++ b/src/test/app/LedgerLoad_test.cpp @@ -36,17 +36,19 @@ class LedgerLoad_test : public beast::unit_test::suite struct SetupData { std::string const dbPath; - std::string ledgerFile{}; - Json::Value ledger{}; - Json::Value hashes{}; - uint256 trapTxHash{}; + // NOLINTBEGIN(readability-redundant-member-init) + std::string ledgerFile = {}; + Json::Value ledger = {}; + Json::Value hashes = {}; + uint256 trapTxHash = {}; + // NOLINTEND(readability-redundant-member-init) }; SetupData setupLedger(beast::temp_dir const& td) { using namespace test::jtx; - SetupData retval = {td.path()}; + SetupData retval = {.dbPath = td.path()}; retval.ledgerFile = td.file("ledgerdata.json"); @@ -58,10 +60,11 @@ class LedgerLoad_test : public beast::unit_test::suite Account acct{"A" + std::to_string(i)}; env.fund(XRP(10000), acct); env.close(); - if (i > 0 && BEAST_EXPECT(prev)) + if (i > 0 && BEAST_EXPECT(prev.has_value())) { - env.trust(acct["USD"](1000), *prev); - env(pay(acct, *prev, acct["USD"](5))); + env.trust(acct["USD"](1000), *prev); // NOLINT(bugprone-unchecked-optional-access) + env(pay( + acct, *prev, acct["USD"](5))); // NOLINT(bugprone-unchecked-optional-access) } env(offer(acct, XRP(100), acct["USD"](1))); env.close(); @@ -106,7 +109,7 @@ class LedgerLoad_test : public beast::unit_test::suite // create a new env with the ledger file specified for startup Env env( *this, - envconfig(ledgerConfig, sd.dbPath, sd.ledgerFile, StartUpType::LOAD_FILE, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, sd.ledgerFile, StartUpType::LoadFile, std::nullopt), nullptr, beast::severities::kDisabled); auto jrb = env.rpc("ledger", "current", "full")[jss::result]; @@ -124,19 +127,19 @@ class LedgerLoad_test : public beast::unit_test::suite // empty path except([&] { - Env env( + Env const env( *this, - envconfig(ledgerConfig, sd.dbPath, "", StartUpType::LOAD_FILE, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, "", StartUpType::LoadFile, std::nullopt), nullptr, beast::severities::kDisabled); }); // file does not exist except([&] { - Env env( + Env const env( *this, envconfig( - ledgerConfig, sd.dbPath, "badfile.json", StartUpType::LOAD_FILE, std::nullopt), + ledgerConfig, sd.dbPath, "badfile.json", StartUpType::LoadFile, std::nullopt), nullptr, beast::severities::kDisabled); }); @@ -155,13 +158,13 @@ class LedgerLoad_test : public beast::unit_test::suite return; except([&] { - Env env( + Env const env( *this, envconfig( ledgerConfig, sd.dbPath, ledgerFileCorrupt.string(), - StartUpType::LOAD_FILE, + StartUpType::LoadFile, std::nullopt), nullptr, beast::severities::kDisabled); @@ -179,7 +182,7 @@ class LedgerLoad_test : public beast::unit_test::suite boost::erase_all(ledgerHash, "\""); Env env( *this, - envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::LOAD, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::Load, std::nullopt), nullptr, beast::severities::kDisabled); auto jrb = env.rpc("ledger", "current", "full")[jss::result]; @@ -200,7 +203,7 @@ class LedgerLoad_test : public beast::unit_test::suite boost::erase_all(ledgerHash, "\""); Env env( *this, - envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::REPLAY, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::Replay, std::nullopt), nullptr, beast::severities::kDisabled); auto const jrb = env.rpc("ledger", "current", "full")[jss::result]; @@ -226,7 +229,7 @@ class LedgerLoad_test : public beast::unit_test::suite boost::erase_all(ledgerHash, "\""); Env env( *this, - envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::REPLAY, sd.trapTxHash), + envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::Replay, sd.trapTxHash), nullptr, beast::severities::kDisabled); auto const jrb = env.rpc("ledger", "current", "full")[jss::result]; @@ -254,9 +257,9 @@ class LedgerLoad_test : public beast::unit_test::suite { // will throw an exception, because we cannot load a ledger for // replay when trapTxHash is set to an invalid transaction - Env env( + Env const env( *this, - envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::REPLAY, ~sd.trapTxHash), + envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::Replay, ~sd.trapTxHash), nullptr, beast::severities::kDisabled); BEAST_EXPECT(false); @@ -280,7 +283,7 @@ class LedgerLoad_test : public beast::unit_test::suite // create a new env with the ledger "latest" specified for startup Env env( *this, - envconfig(ledgerConfig, sd.dbPath, "latest", StartUpType::LOAD, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, "latest", StartUpType::Load, std::nullopt), nullptr, beast::severities::kDisabled); auto jrb = env.rpc("ledger", "current", "full")[jss::result]; @@ -298,7 +301,7 @@ class LedgerLoad_test : public beast::unit_test::suite // create a new env with specific ledger index at startup Env env( *this, - envconfig(ledgerConfig, sd.dbPath, "43", StartUpType::LOAD, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, "43", StartUpType::Load, std::nullopt), nullptr, beast::severities::kDisabled); auto jrb = env.rpc("ledger", "current", "full")[jss::result]; @@ -311,7 +314,7 @@ public: void run() override { - beast::temp_dir td; + beast::temp_dir const td; auto sd = setupLedger(td); // test cases diff --git a/src/test/app/LedgerMaster_test.cpp b/src/test/app/LedgerMaster_test.cpp index 72af492259..9af995b01e 100644 --- a/src/test/app/LedgerMaster_test.cpp +++ b/src/test/app/LedgerMaster_test.cpp @@ -9,7 +9,7 @@ namespace test { class LedgerMaster_test : public beast::unit_test::suite { - std::unique_ptr + static std::unique_ptr makeNetworkConfig(uint32_t networkID) { using namespace jtx; @@ -52,14 +52,14 @@ class LedgerMaster_test : public beast::unit_test::suite // test invalid range { - std::uint32_t ledgerSeq = -1; - std::uint32_t txnIndex = 0; + std::uint32_t const ledgerSeq = -1; + std::uint32_t const txnIndex = 0; auto result = env.app().getLedgerMaster().txnIdFromIndex(ledgerSeq, txnIndex); BEAST_EXPECT(!result); } // test not in ledger { - uint32_t txnIndex = metas[0]->getFieldU32(sfTransactionIndex); + uint32_t const txnIndex = metas[0]->getFieldU32(sfTransactionIndex); auto result = env.app().getLedgerMaster().txnIdFromIndex(0, txnIndex); BEAST_EXPECT(!result); } @@ -70,15 +70,16 @@ class LedgerMaster_test : public beast::unit_test::suite } // ended without result { - uint32_t txnIndex = metas[0]->getFieldU32(sfTransactionIndex); + uint32_t const txnIndex = metas[0]->getFieldU32(sfTransactionIndex); auto result = env.app().getLedgerMaster().txnIdFromIndex(endLegSeq + 1, txnIndex); BEAST_EXPECT(!result); } // success (first tx) { - uint32_t txnIndex = metas[0]->getFieldU32(sfTransactionIndex); + uint32_t const txnIndex = metas[0]->getFieldU32(sfTransactionIndex); auto result = env.app().getLedgerMaster().txnIdFromIndex(startLegSeq, txnIndex); BEAST_EXPECT( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) *result == uint256( "277F4FD89C20B92457FEF05FF63F6405563AD0563C73D967A29727" @@ -86,9 +87,10 @@ class LedgerMaster_test : public beast::unit_test::suite } // success (second tx) { - uint32_t txnIndex = metas[1]->getFieldU32(sfTransactionIndex); + uint32_t const txnIndex = metas[1]->getFieldU32(sfTransactionIndex); auto result = env.app().getLedgerMaster().txnIdFromIndex(startLegSeq + 1, txnIndex); BEAST_EXPECT( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) *result == uint256( "293DF7335EBBAF4420D52E70ABF470EB4C5792CAEA2F91F76193C2" diff --git a/src/test/app/LedgerReplay_test.cpp b/src/test/app/LedgerReplay_test.cpp index 5568a90d03..f9ab08e900 100644 --- a/src/test/app/LedgerReplay_test.cpp +++ b/src/test/app/LedgerReplay_test.cpp @@ -232,9 +232,7 @@ public: bool supportsFeature(ProtocolFeature f) const override { - if (f == ProtocolFeature::LedgerReplay && ledgerReplayEnabled_) - return true; - return false; + return f == ProtocolFeature::LedgerReplay && ledgerReplayEnabled_; } std::optional publisherListSequence(PublicKey const&) const override @@ -248,7 +246,7 @@ public: uint256 const& getClosedLedgerHash() const override { - static uint256 hash{}; + static uint256 const hash{}; return hash; } bool @@ -355,11 +353,15 @@ struct TestPeerSet : public PeerSet { int dropRate = 0; if (behavior == PeerSetBehavior::Drop50) + { dropRate = 50; + } else if (behavior == PeerSetBehavior::DropAll) + { dropRate = 100; + } - if ((rand() % 100 + 1) <= dropRate) + if (((rand() % 100) + 1) <= dropRate) return; switch (type) @@ -396,7 +398,7 @@ struct TestPeerSet : public PeerSet std::set const& getPeerIds() const override { - static std::set emptyPeers; + static std::set const emptyPeers; return emptyPeers; } @@ -462,7 +464,7 @@ struct LedgerServer assert(param.initLedgers > 0); createAccounts(param.initAccounts); createLedgerHistory(); - app.logs().threshold(beast::severities::kWarning); + app.getLogs().threshold(beast::severities::kWarning); } /** @@ -497,7 +499,7 @@ struct LedgerServer auto updateIdx = [&]() { assert(fundedAccounts > senders.size()); fromIdx = (fromIdx + r) % fundedAccounts; - while (senders.count(fromIdx) != 0) + while (senders.contains(fromIdx)) fromIdx = (fromIdx + 1) % fundedAccounts; senders.insert(fromIdx); toIdx = (toIdx + r * 2) % fundedAccounts; @@ -603,7 +605,7 @@ public: bool waitForLedgers(uint256 const& finishLedgerHash, int totalReplay) { - int totalRound = 100; + int const totalRound = 100; for (int i = 0; i < totalRound; ++i) { if (haveLedgers(finishLedgerHash, totalReplay)) @@ -617,12 +619,12 @@ public: bool waitForDone() { - int totalRound = 100; + int const totalRound = 100; for (int i = 0; i < totalRound; ++i) { bool allDone = true; { - std::unique_lock lock(replayer.mtx_); + std::unique_lock const lock(replayer.mtx_); for (auto const& t : replayer.tasks_) { if (!t->finished()) @@ -643,14 +645,14 @@ public: std::vector> getTasks() { - std::unique_lock lock(replayer.mtx_); + std::unique_lock const lock(replayer.mtx_); return replayer.tasks_; } std::shared_ptr findTask(uint256 const& hash, int totalReplay) { - std::unique_lock lock(replayer.mtx_); + std::unique_lock const lock(replayer.mtx_); auto i = std::find_if(replayer.tasks_.begin(), replayer.tasks_.end(), [&](auto const& t) { return t->parameter_.finishHash_ == hash && t->parameter_.totalLedgers_ == totalReplay; }); @@ -662,21 +664,21 @@ public: std::size_t countDeltas() { - std::unique_lock lock(replayer.mtx_); + std::unique_lock const lock(replayer.mtx_); return replayer.deltas_.size(); } std::size_t countSkipLists() { - std::unique_lock lock(replayer.mtx_); + std::unique_lock const lock(replayer.mtx_); return replayer.skipLists_.size(); } bool countsAsExpected(std::size_t tasks, std::size_t skipLists, std::size_t deltas) { - std::unique_lock lock(replayer.mtx_); + std::unique_lock const lock(replayer.mtx_); return replayer.tasks_.size() == tasks && replayer.skipLists_.size() == skipLists && replayer.deltas_.size() == deltas; } @@ -684,7 +686,7 @@ public: std::shared_ptr findSkipListAcquire(uint256 const& hash) { - std::unique_lock lock(replayer.mtx_); + std::unique_lock const lock(replayer.mtx_); auto i = replayer.skipLists_.find(hash); if (i == replayer.skipLists_.end()) return {}; @@ -694,7 +696,7 @@ public: std::shared_ptr findLedgerDeltaAcquire(uint256 const& hash) { - std::unique_lock lock(replayer.mtx_); + std::unique_lock const lock(replayer.mtx_); auto i = replayer.deltas_.find(hash); if (i == replayer.deltas_.end()) return {}; @@ -748,9 +750,7 @@ public: auto t = findTask(hash, totalReplay); if (!t) { - if (taskExpect == TaskStatus::NotExist) - return true; - return false; + return taskExpect == TaskStatus::NotExist; } return asExpected(t, taskExpect, skiplistExpect, deltaExpects); @@ -767,9 +767,7 @@ public: auto t = findTask(hash, totalReplay); if (!t) { - if (taskExpect == TaskStatus::NotExist) - return true; - return false; + return taskExpect == TaskStatus::NotExist; } return asExpected(t, taskExpect, skiplistExpect, deltaExpects); @@ -805,8 +803,8 @@ logAll( LedgerReplayClient& client, beast::severities::Severity level = Severity::kTrace) { - server.app.logs().threshold(level); - client.app.logs().threshold(level); + server.app.getLogs().threshold(level); + client.app.getLogs().threshold(level); } // logAll(net.server, net.client); @@ -968,8 +966,9 @@ struct LedgerReplayer_test : public beast::unit_test::suite { testcase("TaskParameter"); - auto makeSkipList = [](int count) -> std::vector const { + auto makeSkipList = [](int count) -> std::vector { std::vector sList; + sList.reserve(count); for (int i = 0; i < count; ++i) sList.emplace_back(i); return sList; @@ -1018,26 +1017,26 @@ struct LedgerReplayer_test : public beast::unit_test::suite { testcase("config test"); { - Config c; + Config const c; BEAST_EXPECT(c.LEDGER_REPLAY == false); } { Config c; - std::string toLoad(R"rippleConfig( + std::string const toLoad(R"xrpldConfig( [ledger_replay] 1 -)rippleConfig"); +)xrpldConfig"); c.loadFromString(toLoad); BEAST_EXPECT(c.LEDGER_REPLAY == true); } { Config c; - std::string toLoad = (R"rippleConfig( + std::string const toLoad = (R"xrpldConfig( [ledger_replay] 0 -)rippleConfig"); +)xrpldConfig"); c.loadFromString(toLoad); BEAST_EXPECT(c.LEDGER_REPLAY == false); } @@ -1052,20 +1051,18 @@ struct LedgerReplayer_test : public beast::unit_test::suite http_request_type http_request; http_request.version(request.version()); http_request.base() = request.base(); - bool serverResult = peerFeatureEnabled(http_request, FEATURE_LEDGER_REPLAY, server); + bool const serverResult = + peerFeatureEnabled(http_request, FEATURE_LEDGER_REPLAY, server); if (serverResult != expecting) return false; - beast::IP::Address addr = boost::asio::ip::make_address("172.1.1.100"); + beast::IP::Address const addr = boost::asio::ip::make_address("172.1.1.100"); jtx::Env serverEnv(*this); serverEnv.app().config().LEDGER_REPLAY = server; auto http_resp = xrpl::makeResponse( true, http_request, addr, addr, uint256{1}, 1, {1, 0}, serverEnv.app()); auto const clientResult = peerFeatureEnabled(http_resp, FEATURE_LEDGER_REPLAY, client); - if (clientResult != expecting) - return false; - - return true; + return clientResult == expecting; }; BEAST_EXPECT(handshake(false, false, false)); @@ -1085,7 +1082,7 @@ struct LedgerReplayer_test : public beast::unit_test::suite NetworkOfTwo net(*this, {totalReplay + 1}, psBhvr, ilBhvr, peerFeature); auto l = net.server.ledgerMaster.getClosedLedger(); - uint256 finalHash = l->header().hash; + uint256 const finalHash = l->header().hash; for (int i = 0; i < totalReplay; ++i) { BEAST_EXPECT(l); @@ -1095,12 +1092,14 @@ struct LedgerReplayer_test : public beast::unit_test::suite l = net.server.ledgerMaster.getLedgerByHash(l->header().parentHash); } else + { break; + } } net.client.replayer.replay(InboundLedger::Reason::GENERIC, finalHash, totalReplay); - std::vector deltaStatuses(totalReplay - 1, TaskStatus::Completed); + std::vector const deltaStatuses(totalReplay - 1, TaskStatus::Completed); BEAST_EXPECT(net.client.waitAndCheckStatus( finalHash, totalReplay, TaskStatus::Completed, TaskStatus::Completed, deltaStatuses)); @@ -1121,16 +1120,34 @@ struct LedgerReplayer_test : public beast::unit_test::suite PeerFeature::None); auto l = net.server.ledgerMaster.getClosedLedger(); - uint256 finalHash = l->header().hash; + uint256 const finalHash = l->header().hash; net.client.replayer.replay(InboundLedger::Reason::GENERIC, finalHash, totalReplay); - std::vector deltaStatuses(totalReplay - 1, TaskStatus::Completed); + std::vector const deltaStatuses(totalReplay - 1, TaskStatus::Completed); BEAST_EXPECT(net.client.waitAndCheckStatus( finalHash, totalReplay, TaskStatus::Completed, TaskStatus::Completed, deltaStatuses)); - // sweep - net.client.replayer.sweep(); - BEAST_EXPECT(net.client.countsAsExpected(0, 0, 0)); + // sweep() cleans up skipLists_ and deltas_ by removing entries whose + // weak_ptr can no longer be locked. Those weak_ptrs expire only when the + // last shared_ptr holder releases the sub-task. The sole owner is the + // LedgerReplayTask, but a JobQueue worker thread may still hold a + // temporary shared_ptr to a sub-task (from wptr.lock()) while executing + // the timer job that drove the task to completion. If sweep() runs before + // that thread unwinds, the weak_ptr is still lockable and the map entry + // is not removed. We retry until the worker thread finishes. + auto waitForSweep = [&net]() { + for (auto numAttempts = 0; numAttempts < 20; ++numAttempts) + { + net.client.replayer.sweep(); + if (net.client.countsAsExpected(0, 0, 0)) + { + return true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return false; + }; + BEAST_EXPECT(waitForSweep()); } void @@ -1160,7 +1177,7 @@ struct LedgerReplayer_test : public beast::unit_test::suite // feed client with start ledger since InboundLedgers drops all auto l = net.server.ledgerMaster.getClosedLedger(); - uint256 finalHash = l->header().hash; + uint256 const finalHash = l->header().hash; for (int i = 0; i < totalReplay - 1; ++i) { l = net.server.ledgerMaster.getLedgerByHash(l->header().parentHash); @@ -1169,7 +1186,7 @@ struct LedgerReplayer_test : public beast::unit_test::suite net.client.replayer.replay(InboundLedger::Reason::GENERIC, finalHash, totalReplay); - std::vector deltaStatuses(totalReplay - 1, TaskStatus::Completed); + std::vector const deltaStatuses(totalReplay - 1, TaskStatus::Completed); BEAST_EXPECT(net.client.waitAndCheckStatus( finalHash, totalReplay, TaskStatus::Completed, TaskStatus::Completed, deltaStatuses)); BEAST_EXPECT(net.client.waitForLedgers(finalHash, totalReplay)); @@ -1183,7 +1200,7 @@ struct LedgerReplayer_test : public beast::unit_test::suite testStop() { testcase("stop before timeout"); - int totalReplay = 3; + int const totalReplay = 3; NetworkOfTwo net( *this, {totalReplay + 1}, @@ -1192,10 +1209,10 @@ struct LedgerReplayer_test : public beast::unit_test::suite PeerFeature::LedgerReplayEnabled); auto l = net.server.ledgerMaster.getClosedLedger(); - uint256 finalHash = l->header().hash; + uint256 const finalHash = l->header().hash; net.client.replayer.replay(InboundLedger::Reason::GENERIC, finalHash, totalReplay); - std::vector deltaStatuses; + std::vector const deltaStatuses; BEAST_EXPECT(net.client.checkStatus( finalHash, totalReplay, TaskStatus::NotDone, TaskStatus::NotDone, deltaStatuses)); @@ -1208,7 +1225,7 @@ struct LedgerReplayer_test : public beast::unit_test::suite testSkipListBadReply() { testcase("SkipListAcquire bad reply"); - int totalReplay = 3; + int const totalReplay = 3; NetworkOfTwo net( *this, {totalReplay + 1 + 1}, @@ -1217,7 +1234,7 @@ struct LedgerReplayer_test : public beast::unit_test::suite PeerFeature::LedgerReplayEnabled); auto l = net.server.ledgerMaster.getClosedLedger(); - uint256 finalHash = l->header().hash; + uint256 const finalHash = l->header().hash; net.client.replayer.replay(InboundLedger::Reason::GENERIC, finalHash, totalReplay); auto skipList = net.client.findSkipListAcquire(finalHash); @@ -1226,7 +1243,7 @@ struct LedgerReplayer_test : public beast::unit_test::suite auto item = make_shamapitem(uint256(12345), Slice(payload, sizeof(payload))); skipList->processData(l->seq(), item); - std::vector deltaStatuses; + std::vector const deltaStatuses; BEAST_EXPECT(net.client.waitAndCheckStatus( finalHash, totalReplay, TaskStatus::Failed, TaskStatus::Failed, deltaStatuses)); @@ -1241,7 +1258,7 @@ struct LedgerReplayer_test : public beast::unit_test::suite testLedgerDeltaBadReply() { testcase("LedgerDeltaAcquire bad reply"); - int totalReplay = 3; + int const totalReplay = 3; NetworkOfTwo net( *this, {totalReplay + 1}, @@ -1250,7 +1267,7 @@ struct LedgerReplayer_test : public beast::unit_test::suite PeerFeature::LedgerReplayEnabled); auto l = net.server.ledgerMaster.getClosedLedger(); - uint256 finalHash = l->header().hash; + uint256 const finalHash = l->header().hash; net.client.ledgerMaster.storeLedger(l); net.client.replayer.replay(InboundLedger::Reason::GENERIC, finalHash, totalReplay); @@ -1274,15 +1291,15 @@ struct LedgerReplayer_test : public beast::unit_test::suite testLedgerReplayOverlap() { testcase("Overlap tasks"); - int totalReplay = 5; + int const totalReplay = 5; NetworkOfTwo net( *this, - {totalReplay * 3 + 1}, + {(totalReplay * 3) + 1}, PeerSetBehavior::Good, InboundLedgersBehavior::Good, PeerFeature::LedgerReplayEnabled); auto l = net.server.ledgerMaster.getClosedLedger(); - uint256 finalHash = l->header().hash; + uint256 const finalHash = l->header().hash; net.client.replayer.replay(InboundLedger::Reason::GENERIC, finalHash, totalReplay); std::vector deltaStatuses(totalReplay - 1, TaskStatus::Completed); BEAST_EXPECT(net.client.waitAndCheckStatus( @@ -1324,11 +1341,11 @@ struct LedgerReplayer_test : public beast::unit_test::suite TaskStatus::Completed, deltaStatuses)); // deltaStatuses no change BEAST_EXPECT(net.client.waitForLedgers(finalHash_moreEarly, totalReplay)); - BEAST_EXPECT(net.client.countsAsExpected(4, 3, 2 * (totalReplay - 1) + 2)); + BEAST_EXPECT(net.client.countsAsExpected(4, 3, (2 * (totalReplay - 1)) + 2)); // cover net.client.replayer.replay(InboundLedger::Reason::GENERIC, finalHash, totalReplay * 3); - deltaStatuses = std::vector(totalReplay * 3 - 1, TaskStatus::Completed); + deltaStatuses = std::vector((totalReplay * 3) - 1, TaskStatus::Completed); BEAST_EXPECT(net.client.waitAndCheckStatus( finalHash, totalReplay * 3, @@ -1336,7 +1353,7 @@ struct LedgerReplayer_test : public beast::unit_test::suite TaskStatus::Completed, deltaStatuses)); // deltaStatuses changed BEAST_EXPECT(net.client.waitForLedgers(finalHash, totalReplay * 3)); - BEAST_EXPECT(net.client.countsAsExpected(5, 3, totalReplay * 3 - 1)); + BEAST_EXPECT(net.client.countsAsExpected(5, 3, (totalReplay * 3) - 1)); // sweep net.client.replayer.sweep(); @@ -1372,7 +1389,7 @@ struct LedgerReplayerTimeout_test : public beast::unit_test::suite testSkipListTimeout() { testcase("SkipListAcquire timeout"); - int totalReplay = 3; + int const totalReplay = 3; NetworkOfTwo net( *this, {totalReplay + 1}, @@ -1381,10 +1398,10 @@ struct LedgerReplayerTimeout_test : public beast::unit_test::suite PeerFeature::LedgerReplayEnabled); auto l = net.server.ledgerMaster.getClosedLedger(); - uint256 finalHash = l->header().hash; + uint256 const finalHash = l->header().hash; net.client.replayer.replay(InboundLedger::Reason::GENERIC, finalHash, totalReplay); - std::vector deltaStatuses; + std::vector const deltaStatuses; BEAST_EXPECT(net.client.waitAndCheckStatus( finalHash, totalReplay, TaskStatus::Failed, TaskStatus::Failed, deltaStatuses)); @@ -1398,7 +1415,7 @@ struct LedgerReplayerTimeout_test : public beast::unit_test::suite testLedgerDeltaTimeout() { testcase("LedgerDeltaAcquire timeout"); - int totalReplay = 3; + int const totalReplay = 3; NetworkOfTwo net( *this, {totalReplay + 1}, @@ -1407,7 +1424,7 @@ struct LedgerReplayerTimeout_test : public beast::unit_test::suite PeerFeature::LedgerReplayEnabled); auto l = net.server.ledgerMaster.getClosedLedger(); - uint256 finalHash = l->header().hash; + uint256 const finalHash = l->header().hash; net.client.ledgerMaster.storeLedger(l); net.client.replayer.replay(InboundLedger::Reason::GENERIC, finalHash, totalReplay); @@ -1436,11 +1453,11 @@ struct LedgerReplayerLong_test : public beast::unit_test::suite run() override { testcase("Acquire 1000 ledgers"); - int totalReplay = 250; - int rounds = 4; + int const totalReplay = 250; + int const rounds = 4; NetworkOfTwo net( *this, - {totalReplay * rounds + 1}, + {(totalReplay * rounds) + 1}, PeerSetBehavior::Good, InboundLedgersBehavior::Good, PeerFeature::LedgerReplayEnabled); @@ -1463,7 +1480,7 @@ struct LedgerReplayerLong_test : public beast::unit_test::suite InboundLedger::Reason::GENERIC, finishHashes[i], totalReplay); } - std::vector deltaStatuses(totalReplay - 1, TaskStatus::Completed); + std::vector const deltaStatuses(totalReplay - 1, TaskStatus::Completed); for (int i = 0; i < rounds; ++i) { BEAST_EXPECT(net.client.waitAndCheckStatus( diff --git a/src/test/app/LendingHelpers_test.cpp b/src/test/app/LendingHelpers_test.cpp index 24661c18fb..56db553583 100644 --- a/src/test/app/LendingHelpers_test.cpp +++ b/src/test/app/LendingHelpers_test.cpp @@ -593,7 +593,7 @@ class LendingHelpers_test : public beast::unit_test::suite using namespace jtx; using namespace xrpl::detail; - Env env{*this}; + Env const env{*this}; Account const issuer{"issuer"}; PrettyAsset const asset = issuer["USD"]; std::int32_t const loanScale = -5; @@ -680,7 +680,7 @@ class LendingHelpers_test : public beast::unit_test::suite using namespace jtx; using namespace xrpl::detail; - Env env{*this}; + Env const env{*this}; Account const issuer{"issuer"}; PrettyAsset const asset = issuer["USD"]; std::int32_t const loanScale = -5; @@ -773,7 +773,7 @@ class LendingHelpers_test : public beast::unit_test::suite using namespace jtx; using namespace xrpl::detail; - Env env{*this}; + Env const env{*this}; Account const issuer{"issuer"}; PrettyAsset const asset = issuer["USD"]; std::int32_t const loanScale = -5; @@ -872,7 +872,7 @@ class LendingHelpers_test : public beast::unit_test::suite using namespace jtx; using namespace xrpl::detail; - Env env{*this}; + Env const env{*this}; Account const issuer{"issuer"}; PrettyAsset const asset = issuer["USD"]; std::int32_t const loanScale = -5; @@ -979,7 +979,7 @@ class LendingHelpers_test : public beast::unit_test::suite using namespace jtx; using namespace xrpl::detail; - Env env{*this}; + Env const env{*this}; Account const issuer{"issuer"}; PrettyAsset const asset = issuer["USD"]; std::int32_t const loanScale = -5; @@ -1086,7 +1086,7 @@ class LendingHelpers_test : public beast::unit_test::suite using namespace jtx; using namespace xrpl::detail; - Env env{*this}; + Env const env{*this}; Account const issuer{"issuer"}; PrettyAsset const asset = issuer["USD"]; std::int32_t const loanScale = -5; diff --git a/src/test/app/LoadFeeTrack_test.cpp b/src/test/app/LoadFeeTrack_test.cpp index fed76288ef..68ebcc70a1 100644 --- a/src/test/app/LoadFeeTrack_test.cpp +++ b/src/test/app/LoadFeeTrack_test.cpp @@ -13,7 +13,7 @@ public: run() override { Config d; // get a default configuration object - LoadFeeTrack l; + LoadFeeTrack const l; { Fees const fees = [&]() { Fees f; diff --git a/src/test/app/LoanBroker_test.cpp b/src/test/app/LoanBroker_test.cpp index d7c4344ef5..f7222aa61a 100644 --- a/src/test/app/LoanBroker_test.cpp +++ b/src/test/app/LoanBroker_test.cpp @@ -31,7 +31,7 @@ class LoanBroker_test : public beast::unit_test::suite // Try to create a vault PrettyAsset const asset{xrpIssue(), 1'000'000}; - Vault vault{env}; + Vault const vault{env}; auto const [tx, keylet] = vault.create({.owner = alice, .asset = asset}); env(tx, ter(goodVault ? ter(tesSUCCESS) : ter(temDISABLED))); env.close(); @@ -91,12 +91,24 @@ class LoanBroker_test : public beast::unit_test::suite { { auto const& asset = vault.asset.raw(); - testcase << "Lifecycle: " - << (asset.native() ? "XRP " - : asset.holds() ? "IOU " - : asset.holds() ? "MPT " - : "Unknown ") - << label; + std::string_view assetLabel; + if (asset.native()) + { + assetLabel = "XRP "; + } + else if (asset.holds()) + { + assetLabel = "IOU "; + } + else if (asset.holds()) + { + assetLabel = "MPT "; + } + else + { + assetLabel = "Unknown "; + } + testcase << "Lifecycle: " << assetLabel << label; } using namespace jtx; @@ -481,14 +493,14 @@ class LoanBroker_test : public beast::unit_test::suite // MPT. That'll require three corresponding SAVs. Env env(*this, all); - Account issuer{"issuer"}; + Account const issuer{"issuer"}; // For simplicity, alice will be the sole actor for the vault & brokers. Account alice{"alice"}; // Evan will attempt to be naughty Account evan{"evan"}; // Bystander doesn't have anything to do with the SAV or Broker, or any // of the relevant tokens - Account bystander{"bystander"}; + Account const bystander{"bystander"}; Vault vault{env}; // Fund the accounts and trust lines with the same amount so that tests @@ -674,38 +686,28 @@ class LoanBroker_test : public beast::unit_test::suite // fields that can't be changed // LoanBrokerID - env(set(alice, vault.vaultID), - loanBrokerID(nextKeylet.key), - ter(tecNO_ENTRY), - THISLINE); + env(set(alice, vault.vaultID), loanBrokerID(nextKeylet.key), ter(tecNO_ENTRY)); // VaultID - env(set(alice, nextKeylet.key), - loanBrokerID(broker->key()), - ter(tecNO_ENTRY), - THISLINE); + env(set(alice, nextKeylet.key), loanBrokerID(broker->key()), ter(tecNO_ENTRY)); // Owner env(set(evan, vault.vaultID), loanBrokerID(broker->key()), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); // ManagementFeeRate env(set(alice, vault.vaultID), loanBrokerID(broker->key()), managementFeeRate(maxManagementFeeRate), - ter(temINVALID), - THISLINE); + ter(temINVALID)); // CoverRateMinimum env(set(alice, vault.vaultID), loanBrokerID(broker->key()), coverRateMinimum(maxManagementFeeRate), - ter(temINVALID), - THISLINE); + ter(temINVALID)); // CoverRateLiquidation env(set(alice, vault.vaultID), loanBrokerID(broker->key()), coverRateLiquidation(maxManagementFeeRate), - ter(temINVALID), - THISLINE); + ter(temINVALID)); // fields that can be changed testData = "Test Data 1234"; @@ -713,15 +715,13 @@ class LoanBroker_test : public beast::unit_test::suite env(set(alice, vault.vaultID), loanBrokerID(broker->key()), data(std::string(maxDataPayloadLength + 1, 'W')), - ter(temINVALID), - THISLINE); + ter(temINVALID)); // Bad debt maximum env(set(alice, vault.vaultID), loanBrokerID(broker->key()), debtMaximum(Number(-175, -1)), - ter(temINVALID), - THISLINE); + ter(temINVALID)); Number debtMax{175, -1}; if (vault.asset.integral()) { @@ -729,16 +729,14 @@ class LoanBroker_test : public beast::unit_test::suite loanBrokerID(broker->key()), data(testData), debtMaximum(debtMax), - ter(tecPRECISION_LOSS), - THISLINE); + ter(tecPRECISION_LOSS)); roundToAsset(vault.asset, debtMax); } // Data & Debt maximum env(set(alice, vault.vaultID), loanBrokerID(broker->key()), data(testData), - debtMaximum(debtMax), - THISLINE); + debtMaximum(debtMax)); }, [&](SLE::const_ref broker) { // Check the updated fields @@ -809,7 +807,7 @@ class LoanBroker_test : public beast::unit_test::suite Account const issuer{"issuer"}; Account const alice{"alice"}; Env env(*this); - Vault vault{env}; + Vault const vault{env}; env.fund(XRP(100'000), issuer, alice); env.close(); @@ -817,16 +815,16 @@ class LoanBroker_test : public beast::unit_test::suite PrettyAsset const asset = [&]() { if (getAsset) return getAsset(env, issuer, alice); - env(trust(alice, issuer["IOU"](1'000'000)), THISLINE); + env(trust(alice, issuer["IOU"](1'000'000))); env.close(); return PrettyAsset(issuer["IOU"]); }(); - env(pay(issuer, alice, asset(100'000)), THISLINE); + env(pay(issuer, alice, asset(100'000))); env.close(); auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset}); - env(tx, THISLINE); + env(tx); env.close(); auto const le = env.le(vaultKeylet); VaultInfo vaultInfo = [&]() { @@ -837,12 +835,11 @@ class LoanBroker_test : public beast::unit_test::suite if (vaultInfo.vaultID == uint256{}) return; - env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}), - THISLINE); + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)})); env.close(); auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice)); - env(set(alice, vaultInfo.vaultID), THISLINE); + env(set(alice, vaultInfo.vaultID)); env.close(); auto broker = env.le(brokerKeylet); @@ -853,23 +850,23 @@ class LoanBroker_test : public beast::unit_test::suite auto jv = getTxJv(); // empty broker ID jv[sfLoanBrokerID] = ""; - env(jv, ter(temINVALID), THISLINE); + env(jv, ter(temINVALID)); // zero broker ID jv[sfLoanBrokerID] = to_string(uint256{}); // needs a flag to distinguish the parsed STTx from the prior // test - env(jv, txflags(tfFullyCanonicalSig), ter(temINVALID), THISLINE); + env(jv, txflags(tfFullyCanonicalSig), ter(temINVALID)); }; auto testZeroVaultID = [&](auto&& getTxJv) { auto jv = getTxJv(); // empty broker ID jv[sfVaultID] = ""; - env(jv, ter(temINVALID), THISLINE); + env(jv, ter(temINVALID)); // zero broker ID jv[sfVaultID] = to_string(uint256{}); // needs a flag to distinguish the parsed STTx from the prior // test - env(jv, txflags(tfFullyCanonicalSig), ter(temINVALID), THISLINE); + env(jv, txflags(tfFullyCanonicalSig), ter(temINVALID)); }; if (brokerTest == CoverDeposit) @@ -878,26 +875,24 @@ class LoanBroker_test : public beast::unit_test::suite testZeroBrokerID([&]() { return coverDeposit(alice, brokerKeylet.key, asset(10)); }); // preclaim: tecWRONG_ASSET - env(coverDeposit(alice, brokerKeylet.key, issuer["BAD"](10)), - ter(tecWRONG_ASSET), - THISLINE); + env(coverDeposit(alice, brokerKeylet.key, issuer["BAD"](10)), ter(tecWRONG_ASSET)); // preclaim: tecINSUFFICIENT_FUNDS - env(pay(alice, issuer, asset(100'000 - 50)), THISLINE); + env(pay(alice, issuer, asset(100'000 - 50))); env.close(); env(coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)), ter(tecINSUFFICIENT_FUNDS)); // preclaim: tecFROZEN - env(fset(issuer, asfGlobalFreeze), THISLINE); + env(fset(issuer, asfGlobalFreeze)); env.close(); - env(coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)), - ter(tecFROZEN), - THISLINE); + env(coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)), ter(tecFROZEN)); } else + { // Fund the cover deposit - env(coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)), THISLINE); + env(coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10))); + } env.close(); if (brokerTest == CoverWithdraw) @@ -906,59 +901,51 @@ class LoanBroker_test : public beast::unit_test::suite testZeroBrokerID([&]() { return coverWithdraw(alice, brokerKeylet.key, asset(10)); }); // preclaim: tecWRONG_ASSET - env(coverWithdraw(alice, brokerKeylet.key, issuer["BAD"](10)), - ter(tecWRONG_ASSET), - THISLINE); + env(coverWithdraw(alice, brokerKeylet.key, issuer["BAD"](10)), ter(tecWRONG_ASSET)); // preclaim: tecNO_DST Account const bogus{"bogus"}; env(coverWithdraw(alice, brokerKeylet.key, asset(10)), destination(bogus), - ter(tecNO_DST), - THISLINE); + ter(tecNO_DST)); // preclaim: tecDST_TAG_NEEDED Account const dest{"dest"}; env.fund(XRP(1'000), dest); - env(fset(dest, asfRequireDest), THISLINE); + env(fset(dest, asfRequireDest)); env.close(); env(coverWithdraw(alice, brokerKeylet.key, asset(10)), destination(dest), - ter(tecDST_TAG_NEEDED), - THISLINE); + ter(tecDST_TAG_NEEDED)); // preclaim: tecNO_PERMISSION - env(fclear(dest, asfRequireDest), THISLINE); - env(fset(dest, asfDepositAuth), THISLINE); + env(fclear(dest, asfRequireDest)); + env(fset(dest, asfDepositAuth)); env.close(); env(coverWithdraw(alice, brokerKeylet.key, asset(10)), destination(dest), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); // preclaim: tecFROZEN - env(trust(dest, asset(1'000)), THISLINE); - env(fclear(dest, asfDepositAuth), THISLINE); - env(fset(issuer, asfGlobalFreeze), THISLINE); + env(trust(dest, asset(1'000))); + env(fclear(dest, asfDepositAuth)); + env(fset(issuer, asfGlobalFreeze)); env.close(); env(coverWithdraw(alice, brokerKeylet.key, asset(10)), destination(dest), - ter(tecFROZEN), - THISLINE); + ter(tecFROZEN)); // preclaim:: tecFROZEN (deep frozen) - env(fclear(issuer, asfGlobalFreeze), THISLINE); - env(trust(issuer, asset(1'000), dest, tfSetFreeze | tfSetDeepFreeze), THISLINE); + env(fclear(issuer, asfGlobalFreeze)); + env(trust(issuer, asset(1'000), dest, tfSetFreeze | tfSetDeepFreeze)); env(coverWithdraw(alice, brokerKeylet.key, asset(10)), destination(dest), - ter(tecFROZEN), - THISLINE); + ter(tecFROZEN)); // preclaim: tecPSEUDO_ACCOUNT env(coverWithdraw(alice, brokerKeylet.key, asset(10)), destination(vaultInfo.pseudoAccount), - ter(tecPSEUDO_ACCOUNT), - THISLINE); + ter(tecPSEUDO_ACCOUNT)); } if (brokerTest == CoverClawback) @@ -977,17 +964,15 @@ class LoanBroker_test : public beast::unit_test::suite env(coverClawback(issuer), loanBrokerID(brokerKeylet.key), amount(vaultInfo.asset(2)), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); // preclaim: NoFreeze is set - env(fset(issuer, asfAllowTrustLineClawback | asfNoFreeze), THISLINE); + env(fset(issuer, asfAllowTrustLineClawback | asfNoFreeze)); env.close(); env(coverClawback(issuer), loanBrokerID(brokerKeylet.key), amount(vaultInfo.asset(2)), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); } else { @@ -995,8 +980,7 @@ class LoanBroker_test : public beast::unit_test::suite env(coverClawback(issuer), loanBrokerID(brokerKeylet.key), amount(vaultInfo.asset(2)), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); } env.close(); } @@ -1007,30 +991,31 @@ class LoanBroker_test : public beast::unit_test::suite env.fund(XRP(1'000), borrower); env(loan::set(borrower, brokerKeylet.key, asset(50).value()), sig(sfCounterpartySignature, alice), - fee(env.current()->fees().base * 2), - THISLINE); + fee(env.current()->fees().base * 2)); // preflight: temINVALID (empty/zero broker id) testZeroBrokerID([&]() { return del(alice, brokerKeylet.key); }); // preclaim: tecHAS_OBLIGATIONS - env(del(alice, brokerKeylet.key), ter(tecHAS_OBLIGATIONS), THISLINE); + env(del(alice, brokerKeylet.key), ter(tecHAS_OBLIGATIONS)); // Repay and delete the loan auto const loanKeylet = keylet::loan(brokerKeylet.key, 1); - env(loan::pay(borrower, loanKeylet.key, asset(50).value()), THISLINE); - env(loan::del(alice, loanKeylet.key), THISLINE); + env(loan::pay(borrower, loanKeylet.key, asset(50).value())); + env(loan::del(alice, loanKeylet.key)); - env(trust(issuer, asset(0), alice, tfSetFreeze | tfSetDeepFreeze), THISLINE); + env(trust(issuer, asset(0), alice, tfSetFreeze | tfSetDeepFreeze)); // preclaim: tecFROZEN (deep frozen) - env(del(alice, brokerKeylet.key), ter(tecFROZEN), THISLINE); - env(trust(issuer, asset(0), alice, tfClearFreeze | tfClearDeepFreeze), THISLINE); + env(del(alice, brokerKeylet.key), ter(tecFROZEN)); + env(trust(issuer, asset(0), alice, tfClearFreeze | tfClearDeepFreeze)); // successful delete the loan broker object - env(del(alice, brokerKeylet.key), ter(tesSUCCESS), THISLINE); + env(del(alice, brokerKeylet.key), ter(tesSUCCESS)); } else - env(del(alice, brokerKeylet.key), THISLINE); + { + env(del(alice, brokerKeylet.key)); + } if (brokerTest == Set) { @@ -1045,21 +1030,21 @@ class LoanBroker_test : public beast::unit_test::suite if (asset.holds()) { - env(fclear(issuer, asfDefaultRipple), THISLINE); + env(fclear(issuer, asfDefaultRipple)); env.close(); // preclaim: DefaultRipple is not set - env(set(alice, vaultInfo.vaultID), ter(terNO_RIPPLE), THISLINE); + env(set(alice, vaultInfo.vaultID), ter(terNO_RIPPLE)); - env(fset(issuer, asfDefaultRipple), THISLINE); + env(fset(issuer, asfDefaultRipple)); env.close(); } auto const amt = env.balance(alice) - env.current()->fees().accountReserve(env.ownerCount(alice)); - env(pay(alice, issuer, amt), THISLINE); + env(pay(alice, issuer, amt)); // preclaim:: tecINSUFFICIENT_RESERVE - env(set(alice, vaultInfo.vaultID), ter(tecINSUFFICIENT_RESERVE), THISLINE); + env(set(alice, vaultInfo.vaultID), ter(tecINSUFFICIENT_RESERVE)); } } @@ -1082,10 +1067,10 @@ class LoanBroker_test : public beast::unit_test::suite auto jtx = env.jt(coverClawback(alice), amount(USD(100))); // holder == account - env(jtx, ter(temINVALID), THISLINE); + env(jtx, ter(temINVALID)); // holder == beast::zero - STAmount bad(Issue{USD.currency, beast::zero}, 100); + STAmount const bad(Issue{USD.currency, beast::zero}, 100); jtx.jv[sfAmount] = bad.getJson(); jtx.stx = env.ust(jtx); Serializer s; @@ -1106,7 +1091,7 @@ class LoanBroker_test : public beast::unit_test::suite // MPTCanClawback is not set testLoanBroker( [&](Env& env, Account const& issuer, Account const& alice) -> MPT { - MPTTester mpt({.env = env, .issuer = issuer, .holders = {alice}}); + MPTTester const mpt({.env = env, .issuer = issuer, .holders = {alice}}); return mpt; }, CoverClawback); @@ -1186,7 +1171,7 @@ class LoanBroker_test : public beast::unit_test::suite // Create a Vault owned by alice with an XRP asset PrettyAsset const asset{xrpIssue(), 1}; - Vault vault{env}; + Vault const vault{env}; auto const [createTx, vaultKeylet] = vault.create({.owner = alice, .asset = asset}); env(createTx); env.close(); @@ -1208,7 +1193,7 @@ class LoanBroker_test : public beast::unit_test::suite // vault SLE OpenView ov{*env.current()}; test::StreamSink sink{beast::severities::kWarning}; - beast::Journal jlog{sink}; + beast::Journal const jlog{sink}; ApplyContext ac{env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, tapNONE, jlog}; if (auto sleBroker = ac.view().peek(keylet::loanbroker(brokerKeylet.key))) @@ -1222,7 +1207,7 @@ class LoanBroker_test : public beast::unit_test::suite // Invoke preclaim against the mutated (ApplyView) view; triggers // nullptr deref - PreclaimContext pctx{env.app(), ac.view(), tesSUCCESS, tx, tapNONE, jlog}; + PreclaimContext const pctx{env.app(), ac.view(), tesSUCCESS, tx, tapNONE, jlog}; (void)LoanBrokerCoverDeposit::preclaim(pctx); } @@ -1341,7 +1326,7 @@ class LoanBroker_test : public beast::unit_test::suite Account const issuer{"issuer"}; Account const alice{"alice"}; Env env(*this); - Vault vault{env}; + Vault const vault{env}; env.fund(XRP(100'000), issuer, alice); env.close(); @@ -1356,14 +1341,14 @@ class LoanBroker_test : public beast::unit_test::suite return mptAsset; }(); - env(pay(issuer, alice, asset(100'000)), THISLINE); + env(pay(issuer, alice, asset(100'000))); env.close(); auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset}); - env(tx, THISLINE); + env(tx); env.close(); auto const le = env.le(vaultKeylet); - VaultInfo vaultInfo = [&]() { + VaultInfo const vaultInfo = [&]() { if (BEAST_EXPECT(le)) return VaultInfo{asset, vaultKeylet.key, le->at(sfAccount)}; return VaultInfo{asset, {}, {}}; @@ -1371,20 +1356,18 @@ class LoanBroker_test : public beast::unit_test::suite if (vaultInfo.vaultID == uint256{}) return; - env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}), - THISLINE); + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)})); env.close(); auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice)); - env(set(alice, vaultInfo.vaultID), THISLINE); + env(set(alice, vaultInfo.vaultID)); env.close(); Account const borrower{"borrower"}; env.fund(XRP(1'000), borrower); env(loan::set(borrower, brokerKeylet.key, asset(50).value()), sig(sfCounterpartySignature, alice), - fee(env.current()->fees().base * 2), - THISLINE); + fee(env.current()->fees().base * 2)); auto const broker = env.le(brokerKeylet); if (!BEAST_EXPECT(broker)) return; @@ -1395,47 +1378,47 @@ class LoanBroker_test : public beast::unit_test::suite auto tx2 = set(alice, vaultInfo.vaultID); tx2[sfLoanBrokerID] = to_string(brokerKeylet.key); tx2[sfDebtMaximum] = debtTotal - 1; - env(tx2, ter(tecLIMIT_EXCEEDED), THISLINE); + env(tx2, ter(tecLIMIT_EXCEEDED)); tx2[sfDebtMaximum] = debtTotal + 1; - env(tx2, ter(tesSUCCESS), THISLINE); + env(tx2, ter(tesSUCCESS)); tx2[sfDebtMaximum] = 0; - env(tx2, ter(tesSUCCESS), THISLINE); + env(tx2, ter(tesSUCCESS)); tx2[sfDebtMaximum] = Json::Value::maxInt; - env(tx2, ter(tesSUCCESS), THISLINE); + env(tx2, ter(tesSUCCESS)); { auto const dm = power(2, 64) - 1; BEAST_EXPECT(dm > maxMPTokenAmount); tx2[sfDebtMaximum] = dm; - env(tx2, ter(temINVALID), THISLINE); + env(tx2, ter(temINVALID)); } { auto const dm = power(2, 63) - 1; BEAST_EXPECTS(dm > maxMPTokenAmount, to_string(dm)); tx2[sfDebtMaximum] = dm; - env(tx2, ter(temINVALID), THISLINE); + env(tx2, ter(temINVALID)); } { auto const dm = power(2, 63) - 3; BEAST_EXPECTS(dm == maxMPTokenAmount, to_string(dm)); tx2[sfDebtMaximum] = dm; - env(tx2, ter(tesSUCCESS), THISLINE); + env(tx2, ter(tesSUCCESS)); } { auto const dm = 2 * (power(2, 62) - 1) + 1; BEAST_EXPECTS(dm == maxMPTokenAmount, to_string(dm)); tx2[sfDebtMaximum] = dm; - env(tx2, ter(tesSUCCESS), THISLINE); + env(tx2, ter(tesSUCCESS)); } tx2[sfDebtMaximum] = Number{9223372036854775807, 0}; - env(tx2, ter(tesSUCCESS), THISLINE); + env(tx2, ter(tesSUCCESS)); } void @@ -1455,7 +1438,7 @@ class LoanBroker_test : public beast::unit_test::suite auto const [token, deposit, err] = getToken(env); - Vault vault(env); + Vault const vault(env); auto const [tx, keylet] = vault.create({.owner = broker, .asset = token.asset()}); env(tx); env.close(); @@ -1483,7 +1466,7 @@ class LoanBroker_test : public beast::unit_test::suite std::optional, // max amount std::uint64_t, // deposit amount TER>> // expected error - mptTests = { + const mptTests = { // issuer can issue up to 2'000 tokens {2'000, 4'000, 1'000, tesSUCCESS}, // issuer can issue 500 tokens (250 VaultDeposit + @@ -1526,7 +1509,7 @@ class LoanBroker_test : public beast::unit_test::suite env.fund(XRP(20'000), issuer, lender, borrower); auto const IOU = issuer["IOU"]; - Vault vault{env}; + Vault const vault{env}; auto [tx, vaultKeylet] = vault.create({.owner = lender, .asset = IOU.asset()}); env(tx); env.close(); @@ -1614,7 +1597,7 @@ class LoanBroker_test : public beast::unit_test::suite env(pay(issuer, broker, token(2'000))); env.close(); - Vault vault(env); + Vault const vault(env); auto const [tx, keylet] = vault.create({.owner = broker, .asset = token.asset()}); env(tx); env.close(); @@ -1735,7 +1718,7 @@ class LoanBroker_test : public beast::unit_test::suite auto const& token = *maybeToken; - Vault vault(env); + Vault const vault(env); auto const [tx, keylet] = vault.create({.owner = broker, .asset = token.asset()}); env(tx); env.close(); @@ -1753,7 +1736,7 @@ class LoanBroker_test : public beast::unit_test::suite BEAST_EXPECT(env.ter() == err); env.close(); - if (err != tesSUCCESS) + if (!isTesSuccess(err)) { env(vault.withdraw( {.depositor = broker, .id = keylet.key, .amount = token(1'000)})); diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index a0224148c0..05123c11c0 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -81,13 +81,13 @@ protected: int coverDeposit = 1000; TenthBips16 managementFeeRate{100}; TenthBips32 coverRateLiquidation = percentageToTenthBips(25); - std::string data{}; + std::string data = {}; // NOLINT(readability-redundant-member-init) std::uint32_t flags = 0; Number maxCoveredLoanValue(Number const& currentDebt) const { - NumberRoundModeGuard mg(Number::downward); + NumberRoundModeGuard const mg(Number::downward); auto debtLimit = coverDeposit * tenthBipsPerUnity.value() / coverRateMin.value(); return debtLimit - currentDebt; @@ -150,20 +150,22 @@ protected: // only signs. bool counterpartyExplicit = true; Number principalRequest; - std::optional setFee{}; - std::optional originationFee{}; - std::optional serviceFee{}; - std::optional lateFee{}; - std::optional closeFee{}; - std::optional overFee{}; - std::optional interest{}; - std::optional lateInterest{}; - std::optional closeInterest{}; - std::optional overpaymentInterest{}; - std::optional payTotal{}; - std::optional payInterval{}; - std::optional gracePd{}; - std::optional flags{}; + // NOLINTBEGIN(readability-redundant-member-init) + std::optional setFee = std::nullopt; + std::optional originationFee = std::nullopt; + std::optional serviceFee = std::nullopt; + std::optional lateFee = std::nullopt; + std::optional closeFee = std::nullopt; + std::optional overFee = std::nullopt; + std::optional interest = std::nullopt; + std::optional lateInterest = std::nullopt; + std::optional closeInterest = std::nullopt; + std::optional overpaymentInterest = std::nullopt; + std::optional payTotal = std::nullopt; + std::optional payInterval = std::nullopt; + std::optional gracePd = std::nullopt; + std::optional flags = std::nullopt; + // NOLINTEND(readability-redundant-member-init) template jtx::JTx @@ -232,7 +234,7 @@ protected: struct LoanState { std::uint32_t previousPaymentDate = 0; - NetClock::time_point startDate = {}; + NetClock::time_point startDate; std::uint32_t nextPaymentDate = 0; std::uint32_t paymentRemaining = 0; std::int32_t const loanScale = 0; @@ -386,7 +388,7 @@ protected: if (auto vaultSle = env.le(keylet::vault(brokerSle->at(sfVaultID))); env.test.BEAST_EXPECT(vaultSle)) { - if ((flags & lsfLoanImpaired) && !(flags & lsfLoanDefault)) + if (((flags & lsfLoanImpaired) != 0u) && ((flags & lsfLoanDefault) == 0u)) { env.test.BEAST_EXPECT( vaultSle->at(sfLossUnrealized) == @@ -427,7 +429,7 @@ protected: { using namespace jtx; - Vault vault{env}; + Vault const vault{env}; auto const deposit = asset(params.vaultDeposit); auto const debtMaximumValue = asset(params.debtMax).value(); @@ -518,7 +520,7 @@ protected: : std::max(broker.vaultScale(env), state.principalOutstanding.exponent()))); BEAST_EXPECT(state.paymentInterval == 600); { - NumberRoundModeGuard mg(Number::upward); + NumberRoundModeGuard const mg(Number::upward); BEAST_EXPECT( state.totalValue == roundToAsset( @@ -698,10 +700,12 @@ protected: env.close(); if (asset.native() || lender != issuer) + { env( pay((asset.native() ? env.master : issuer), lender, asset(brokerParams.vaultDeposit + brokerParams.coverDeposit))); + } // Fund the borrower later once we know the total loan // size @@ -742,7 +746,7 @@ protected: return std::make_tuple(broker, loanKeylet, pseudoAcct); } - void + static void topUpBorrower( jtx::Env& env, BrokerInfo const& broker, @@ -773,10 +777,12 @@ protected: auto const shortage = totalNeeded - borrowerBalance.number(); if (shortage > beast::zero && (broker.asset.native() || issuer != borrower)) + { env( pay((broker.asset.native() ? env.master : issuer), borrower, STAmount{broker.asset, shortage})); + } } void @@ -826,6 +832,7 @@ protected: roundPeriodicPayment(broker.asset, state.periodicPayment, state.loanScale)}; if (!showStepBalances) + { log << currencyLabel << " Payment components: " << "Payments remaining, " << "rawInterest, rawPrincipal, " @@ -833,6 +840,7 @@ protected: << "trackedValueDelta, trackedPrincipalDelta, " "trackedInterestDelta, trackedMgmtFeeDelta, special" << std::endl; + } // Include the service fee STAmount const totalDue = @@ -947,18 +955,21 @@ protected: (state.loanScale - (deltas.total() - state.periodicPayment).exponent()) > 14); if (!showStepBalances) + { log << currencyLabel << " Payment components: " << state.paymentRemaining << ", " << deltas.interest << ", " << deltas.principal << ", " << deltas.managementFee << ", " << paymentComponents.trackedValueDelta << ", " << paymentComponents.trackedPrincipalDelta << ", " << paymentComponents.trackedInterestPart() << ", " - << paymentComponents.trackedManagementFeeDelta << ", " - << (paymentComponents.specialCase == detail::PaymentSpecialCase::final ? "final" - : paymentComponents.specialCase == detail::PaymentSpecialCase::extra - ? "extra" - : "none") - << std::endl; + << paymentComponents.trackedManagementFeeDelta << ", " << [&]() -> char const* { + if (paymentComponents.specialCase == detail::PaymentSpecialCase::final) + return "final"; + if (paymentComponents.specialCase == detail::PaymentSpecialCase::extra) + return "extra"; + return "none"; + }() << std::endl; + } auto const totalDueAmount = STAmount{broker.asset, paymentComponents.trackedValueDelta + serviceFee}; @@ -1016,8 +1027,10 @@ protected: { auto const loanSle = env.le(loanKeylet); if (!BEAST_EXPECT(loanSle)) + { // No reason for this not to exist return; + } auto const current = constructRoundedLoanState(loanSle); auto const errors = nextTrueState - current; log << currencyLabel << " Loan balances: " @@ -1091,8 +1104,10 @@ protected: { auto const loanSle = env.le(loanKeylet); if (!BEAST_EXPECT(loanSle)) + { // No reason for this not to exist return; + } log << currencyLabel << " Total amounts paid: " << "\n\tTotal value: " << totalPaid.trackedValueDelta << " (initial: " << truncate(initialState.totalValue) @@ -1127,14 +1142,14 @@ protected: auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); - if (!BEAST_EXPECT(loanResult)) + if (BEAST_EXPECT(loanResult); !loanResult.has_value()) return; auto broker = std::get(*loanResult); auto loanKeylet = std::get(*loanResult); auto pseudoAcct = std::get(*loanResult); - VerifyLoanStatus verifyLoanStatus(env, broker, pseudoAcct, loanKeylet); + VerifyLoanStatus const verifyLoanStatus(env, broker, pseudoAcct, loanKeylet); makeLoanPayments( env, @@ -1179,8 +1194,10 @@ protected: auto const [keylet, loanSequence] = [&]() { auto const brokerSle = env.le(keylet::loanbroker(broker.brokerID)); if (!BEAST_EXPECT(brokerSle)) + { // will be invalid return std::make_pair(keylet::loan(broker.brokerID), std::uint32_t(0)); + } // Broker has no loans BEAST_EXPECT(brokerSle->at(sfOwnerCount) == 0); @@ -1422,7 +1439,7 @@ protected: // Either the borrower or the lender can delete the loan. Alternate // between who does it across tests. static unsigned deleteCounter = 0; - auto const deleter = ++deleteCounter % 2 ? lender : borrower; + auto const deleter = ((++deleteCounter % 2) != 0u) ? lender : borrower; env(del(deleter, keylet.key)); env.close(); @@ -1451,14 +1468,16 @@ protected: } } - std::string + static std::string getCurrencyLabel(Asset const& asset) { - return ( - asset.native() ? "XRP" - : asset.holds() ? "IOU" - : asset.holds() ? "MPT" - : "Unknown"); + if (asset.native()) + return "XRP"; + if (asset.holds()) + return "IOU"; + if (asset.holds()) + return "MPT"; + return "Unknown"; } /** Wrapper to run a series of lifecycle tests for a given asset and loan @@ -1520,7 +1539,7 @@ protected: auto const pseudoAcct = [&]() { auto const brokerSle = env.le(keylet::loanbroker(broker.brokerID)); if (!BEAST_EXPECT(brokerSle)) - return lender; + return Account{lender}; auto const brokerPseudo = brokerSle->at(sfAccount); return Account("Broker pseudo-account", brokerPseudo); }(); @@ -1788,9 +1807,11 @@ protected: auto const vaultPseudo = [&]() { auto const vaultSle = env.le(keylet::vault(brokerSle->at(sfVaultID))); if (!BEAST_EXPECT(vaultSle)) + { // This will be wrong, but the test has failed anyway. - return lender; - auto const vaultPseudo = Account("Vault pseudo-account", vaultSle->at(sfAccount)); + return Account{lender}; + } + auto vaultPseudo = Account("Vault pseudo-account", vaultSle->at(sfAccount)); return vaultPseudo; }(); @@ -1801,13 +1822,13 @@ protected: std::function, TER> { // Freeze / lock the asset - std::function empty; + std::function const empty; if (broker.asset.native()) { // XRP can't be frozen return std::make_tuple(empty, empty, empty, tesSUCCESS); } - else if (broker.asset.holds()) + if (broker.asset.holds()) { auto freeze = [&](Account const& holder) { env(trust(issuer, holder[iouCurrency](0), tfSetFreeze)); @@ -1821,16 +1842,14 @@ protected: }; return std::make_tuple(freeze, deepfreeze, unfreeze, tecFROZEN); } - else - { - auto freeze = [&](Account const& holder) { - mptt.set({.account = issuer, .holder = holder, .flags = tfMPTLock}); - }; - auto unfreeze = [&](Account const& holder) { - mptt.set({.account = issuer, .holder = holder, .flags = tfMPTUnlock}); - }; - return std::make_tuple(freeze, empty, unfreeze, tecLOCKED); - } + + auto freeze = [&](Account const& holder) { + mptt.set({.account = issuer, .holder = holder, .flags = tfMPTLock}); + }; + auto unfreeze = [&](Account const& holder) { + mptt.set({.account = issuer, .holder = holder, .flags = tfMPTUnlock}); + }; + return std::make_tuple(freeze, empty, unfreeze, tecLOCKED); }(); // Try freezing the accounts that can't be frozen @@ -1924,7 +1943,7 @@ protected: ? 0 : std::max( broker.vaultScale(env), state.principalOutstanding.exponent()))); - NumberRoundModeGuard mg(Number::upward); + NumberRoundModeGuard const mg(Number::upward); auto const defaultAmount = roundToAsset( broker.asset, std::min( @@ -2052,7 +2071,19 @@ protected: STAmount{broker.asset, state.periodicPayment * Number{15, -1}}, tfLoanOverpayment), fee(XRPAmount{baseFee * (Number{15, -1} / loanPaymentsPerFeeIncrement + 1)}), - ter(temINVALID_FLAG)); + ter(tecNO_PERMISSION)); + + { + env.disableFeature(fixSecurity3_1_3); + env(pay(borrower, + loanKeylet.key, + STAmount{broker.asset, state.periodicPayment * Number{15, -1}}, + tfLoanOverpayment), + fee(XRPAmount{ + baseFee * (Number{15, -1} / loanPaymentsPerFeeIncrement + 1)}), + ter(temINVALID_FLAG)); + env.enableFeature(fixSecurity3_1_3); + } } // Try to send a payment marked as multiple mutually exclusive // payment types. Do not include `txFlags`, so we don't duplicate @@ -2130,7 +2161,7 @@ protected: state.totalValue = 0; state.managementFeeOutstanding = 0; state.previousPaymentDate = - state.nextPaymentDate + state.paymentInterval * (numPayments - 1); + state.nextPaymentDate + (state.paymentInterval * (numPayments - 1)); state.nextPaymentDate = 0; verifyLoanStatus(state); @@ -2216,7 +2247,7 @@ protected: // service fee is 2 auto const startingPayments = state.paymentRemaining; STAmount const payoffAmount = [&]() { - NumberRoundModeGuard mg(Number::upward); + NumberRoundModeGuard const mg(Number::upward); auto const rawPayoff = startingPayments * (state.periodicPayment + broker.asset(2).value()); STAmount payoffAmount{broker.asset, rawPayoff}; @@ -2511,12 +2542,13 @@ protected: << ", " << paymentComponents.trackedPrincipalDelta << ", " << paymentComponents.trackedInterestPart() << ", " << paymentComponents.trackedManagementFeeDelta << ", " - << (paymentComponents.specialCase == detail::PaymentSpecialCase::final - ? "final" - : paymentComponents.specialCase == - detail::PaymentSpecialCase::extra - ? "extra" - : "none"); + << [&]() -> char const* { + if (paymentComponents.specialCase == detail::PaymentSpecialCase::final) + return "final"; + if (paymentComponents.specialCase == detail::PaymentSpecialCase::extra) + return "extra"; + return "none"; + }(); auto const totalDueAmount = STAmount{ broker.asset, paymentComponents.trackedValueDelta + serviceFee.number()}; @@ -2568,8 +2600,10 @@ protected: auto const borrowerBalanceBeforePayment = env.balance(borrower, broker.asset); if (canImpairLoan(env, broker, state)) + { // Making a payment will unimpair the loan env(manage(lender, loanKeylet.key, tfLoanImpair)); + } env.close(); @@ -2813,7 +2847,7 @@ protected: {.flags = tfMPTCanTransfer | tfMPTCanLock | (args.requireAuth ? tfMPTRequireAuth : none)}); env.close(); - PrettyAsset mptAsset = mptt.issuanceID(); + PrettyAsset const mptAsset = mptt.issuanceID(); mptt.authorize({.account = lender}); mptt.authorize({.account = borrower}); env.close(); @@ -2853,6 +2887,7 @@ protected: // Create vaults and loan brokers std::array const assets{mptAsset, iouAsset}; std::vector brokers; + brokers.reserve(assets.size()); for (auto const& asset : assets) { brokers.emplace_back(createVaultAndBroker(env, asset, lender)); @@ -2939,7 +2974,7 @@ protected: CaseArgs{.requireAuth = true}); auto const [acctReserve, incReserve] = [this]() -> std::pair { - Env env{*this, testable_amendments()}; + Env const env{*this, testable_amendments()}; return { env.current()->fees().accountReserve(0).drops() / DROPS_PER_XRP.drops(), env.current()->fees().increment.drops() / DROPS_PER_XRP.drops()}; @@ -2961,7 +2996,7 @@ protected: BEAST_EXPECT(sleMPT1 == nullptr); // Burn some XRP - env(noop(borrower), fee(XRP(acctReserve * 2 + incReserve * 2))); + env(noop(borrower), fee(XRP((acctReserve * 2) + (incReserve * 2)))); env.close(); // Cannot create loan, not enough reserve to create MPToken @@ -2985,7 +3020,7 @@ protected: BEAST_EXPECT(sleMPT2 != nullptr); }, {}, - CaseArgs{.initialXRP = acctReserve * 2 + incReserve * 8 + 1}); + CaseArgs{.initialXRP = (acctReserve * 2) + (incReserve * 8) + 1}); testCase( {}, @@ -3007,7 +3042,7 @@ protected: BEAST_EXPECT(sleLine1 == nullptr); // Burn some XRP - env(noop(borrower), fee(XRP(acctReserve * 2 + incReserve * 2))); + env(noop(borrower), fee(XRP((acctReserve * 2) + (incReserve * 2)))); env.close(); // Cannot create loan, not enough reserve to create trust line @@ -3030,7 +3065,7 @@ protected: auto const sleLine2 = env.le(trustline); BEAST_EXPECT(sleLine2 != nullptr); }, - CaseArgs{.initialXRP = acctReserve * 2 + incReserve * 8 + 1}); + CaseArgs{.initialXRP = (acctReserve * 2) + (incReserve * 8) + 1}); testCase( [&, this](Env& env, BrokerInfo const& broker, MPTTester& mptt) { @@ -3080,7 +3115,7 @@ protected: BEAST_EXPECT(sleMPT3 != nullptr); }, {}, - CaseArgs{.initialXRP = acctReserve * 2 + incReserve * 8 + 1}); + CaseArgs{.initialXRP = (acctReserve * 2) + (incReserve * 8) + 1}); testCase( {}, @@ -3130,7 +3165,7 @@ protected: auto const sleLine3 = env.le(trustline); BEAST_EXPECT(sleLine3 != nullptr); }, - CaseArgs{.initialXRP = acctReserve * 2 + incReserve * 8 + 1}); + CaseArgs{.initialXRP = (acctReserve * 2) + (incReserve * 8) + 1}); testCase( [&, this](Env& env, BrokerInfo const& broker, MPTTester& mptt) { @@ -3224,7 +3259,7 @@ protected: jtx::Account const alice{"alice"}; jtx::Account const bella{"bella"}; auto const msigSetup = [&](Env& env, Account const& account) { - Json::Value tx1 = signers(account, 2, {{alice, 1}, {bella, 1}}); + Json::Value const tx1 = signers(account, 2, {{alice, 1}, {bella, 1}}); env(tx1); env.close(); }; @@ -3291,7 +3326,7 @@ protected: [&, this](Env& env, BrokerInfo const& broker, auto&) { using namespace loan; Number const principalRequest = broker.asset(1'000).value(); - Vault vault{env}; + Vault const vault{env}; auto tx = vault.set({.owner = lender, .id = broker.vaultID}); tx[sfAssetsMaximum] = BrokerParameters::defaults().vaultDeposit; env(tx); @@ -3303,8 +3338,7 @@ protected: interestRate(TenthBips32(10'000)), sig(sfCounterpartySignature, lender), fee(env.current()->fees().base * 5), - ter(tecLIMIT_EXCEEDED), - THISLINE); + ter(tecLIMIT_EXCEEDED)); }, nullptr); @@ -3312,7 +3346,7 @@ protected: [&, this](Env& env, BrokerInfo const& broker, auto&) { using namespace loan; Number const principalRequest = broker.asset(1'000).value(); - Vault vault{env}; + Vault const vault{env}; auto tx = vault.set({.owner = lender, .id = broker.vaultID}); tx[sfAssetsMaximum] = BrokerParameters::defaults().vaultDeposit + broker.asset(1).number(); @@ -3327,8 +3361,7 @@ protected: fee(env.current()->fees().base * 5), paymentTotal(2), paymentInterval(3600 * 24), - ter(tecLIMIT_EXCEEDED), - THISLINE); + ter(tecLIMIT_EXCEEDED)); }, nullptr); } @@ -3388,6 +3421,7 @@ protected: // Create vaults and loan brokers std::vector brokers; + brokers.reserve(assets.size()); for (auto const& asset : assets) { brokers.emplace_back(createVaultAndBroker( @@ -3579,13 +3613,13 @@ protected: Account const lender{"lender"}; Account const borrower{"borrower"}; - BrokerParameters brokerParams; + BrokerParameters const brokerParams; env.fund(XRP(brokerParams.vaultDeposit * 100), lender, borrower); env.close(); PrettyAsset const xrpAsset{xrpIssue(), 1'000'000}; - BrokerInfo broker{createVaultAndBroker(env, xrpAsset, lender, brokerParams)}; + BrokerInfo const broker{createVaultAndBroker(env, xrpAsset, lender, brokerParams)}; using namespace loan; @@ -3642,13 +3676,13 @@ protected: Account const issuer{"issuer"}; Account const lender{"lender"}; - BrokerParameters brokerParams{.debtMax = 0}; + BrokerParameters const brokerParams{.debtMax = 0}; env.fund(XRP(brokerParams.vaultDeposit * 100), issuer, noripple(lender)); env.close(); PrettyAsset const xrpAsset{xrpIssue(), 1'000'000}; - BrokerInfo broker{createVaultAndBroker(env, xrpAsset, lender, brokerParams)}; + BrokerInfo const broker{createVaultAndBroker(env, xrpAsset, lender, brokerParams)}; if (auto const brokerSle = env.le(keylet::loanbroker(broker.brokerID)); BEAST_EXPECT(brokerSle)) @@ -3690,12 +3724,12 @@ protected: Account const lender{"lender"}; Account const borrower{"borrower"}; - BrokerParameters brokerParams; + BrokerParameters const brokerParams; env.fund(XRP(brokerParams.vaultDeposit * 100), issuer, lender, borrower); env.close(); PrettyAsset const xrpAsset{xrpIssue(), 1'000'000}; - BrokerInfo broker{createVaultAndBroker(env, xrpAsset, lender, brokerParams)}; + BrokerInfo const broker{createVaultAndBroker(env, xrpAsset, lender, brokerParams)}; using namespace loan; @@ -3727,7 +3761,7 @@ protected: createJson = env.json(createJson, sig(sfCounterpartySignature, lender)); // Fails in preclaim because principal requested can't be // represented as XRP - env(createJson, ter(tecPRECISION_LOSS), THISLINE); + env(createJson, ter(tecPRECISION_LOSS)); env.close(); BEAST_EXPECT(!env.le(keylet)); @@ -3739,7 +3773,7 @@ protected: createJson = env.json(createJson, sig(sfCounterpartySignature, lender)); // Fails in doApply because the payment is too small to be // represented as XRP. - env(createJson, ter(tecPRECISION_LOSS), THISLINE); + env(createJson, ter(tecPRECISION_LOSS)); env.close(); } @@ -3764,9 +3798,9 @@ protected: Account const alice{"alice"}; std::string const borrowerPass = "borrower"; - Account borrower{borrowerPass, KeyType::ed25519}; + Account const borrower{borrowerPass, KeyType::ed25519}; auto const lenderPass = "lender"; - Account lender{lenderPass, KeyType::ed25519}; + Account const lender{lenderPass, KeyType::ed25519}; env.fund(XRP(1'000'000), alice, lender, borrower); env.close(); @@ -4147,7 +4181,8 @@ protected: env.fund(XRP(1'000), issuer, lender); std::int64_t constexpr issuerBalance = 10'000'000; - MPTTester asset({.env = env, .issuer = issuer, .holders = {lender}, .pay = issuerBalance}); + MPTTester const asset( + {.env = env, .issuer = issuer, .holders = {lender}, .pay = issuerBalance}); BrokerParameters const brokerParams{ .debtMax = 200, @@ -4217,8 +4252,8 @@ protected: // preclaim Env env(*this); env.fund(XRP(1'000), lender, issuer, borrower); - env(trust(lender, IOU(10'000'000)), THISLINE); - env(pay(issuer, lender, IOU(5'000'000)), THISLINE); + env(trust(lender, IOU(10'000'000))); + env(pay(issuer, lender, IOU(5'000'000))); BrokerInfo brokerInfo{createVaultAndBroker(env, issuer["IOU"], lender)}; auto const loanSetFee = fee(env.current()->fees().base * 2); @@ -4226,22 +4261,21 @@ protected: env(set(borrower, brokerInfo.brokerID, debtMaximumRequest), sig(sfCounterpartySignature, lender), - loanSetFee, - THISLINE); + loanSetFee); env.close(); std::uint32_t const loanSequence = 1; auto const loanKeylet = keylet::loan(brokerInfo.brokerID, loanSequence); - env(fset(issuer, asfGlobalFreeze), THISLINE); + env(fset(issuer, asfGlobalFreeze)); env.close(); // preclaim: tecFROZEN - env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecFROZEN), THISLINE); + env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecFROZEN)); env.close(); - env(fclear(issuer, asfGlobalFreeze), THISLINE); + env(fclear(issuer, asfGlobalFreeze)); env.close(); auto const pseudoBroker = [&]() -> std::optional { @@ -4250,42 +4284,36 @@ protected: { return Account{"pseudo", brokerSle->at(sfAccount)}; } - else - { - return std::nullopt; - } + + return std::nullopt; }(); if (!pseudoBroker) return; // Lender and pseudoaccount must both be frozen - env(trust(issuer, lender["IOU"](1'000), lender, tfSetFreeze | tfSetDeepFreeze), THISLINE); + env(trust(issuer, lender["IOU"](1'000), lender, tfSetFreeze | tfSetDeepFreeze)); env(trust( - issuer, - (*pseudoBroker)["IOU"](1'000), - *pseudoBroker, - tfSetFreeze | tfSetDeepFreeze), - THISLINE); + issuer, (*pseudoBroker)["IOU"](1'000), *pseudoBroker, tfSetFreeze | tfSetDeepFreeze)); env.close(); // preclaim: tecFROZEN due to deep frozen - env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecFROZEN), THISLINE); + env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecFROZEN)); env.close(); // Only one needs to be unfrozen - env(trust(issuer, lender["IOU"](1'000), tfClearFreeze | tfClearDeepFreeze), THISLINE); + env(trust(issuer, lender["IOU"](1'000), tfClearFreeze | tfClearDeepFreeze)); env.close(); // The payment is late by this point - env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecEXPIRED), THISLINE); + env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecEXPIRED)); env.close(); - env(pay(borrower, loanKeylet.key, debtMaximumRequest, tfLoanLatePayment), THISLINE); + env(pay(borrower, loanKeylet.key, debtMaximumRequest, tfLoanLatePayment)); env.close(); // preclaim: tecKILLED // note that tecKILLED in loanMakePayment() // doesn't happen because of the preclaim check. - env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecKILLED), THISLINE); + env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecKILLED)); } void @@ -4304,7 +4332,7 @@ protected: env.fund(XRP(1'000), lender, issuer, borrower); env(trust(lender, IOU(10'000'000))); env(pay(issuer, lender, IOU(5'000'000))); - BrokerInfo brokerInfo{createVaultAndBroker(env, issuer["IOU"], lender)}; + BrokerInfo const brokerInfo{createVaultAndBroker(env, issuer["IOU"], lender)}; auto const loanSetFee = fee(env.current()->fees().base * 2); Number const debtMaximumRequest = brokerInfo.asset(1'000).value(); @@ -4366,7 +4394,7 @@ protected: // can it happen? the signature is checked before transactor // executes - JTx tx = env.jt( + JTx const tx = env.jt( set(borrower, brokerInfo.brokerID, debtMaximumRequest), sig(sfCounterpartySignature, lender), loanSetFee); @@ -4455,7 +4483,7 @@ protected: env(pay(issuer, borrower, mptAsset(1'000))); env.close(); - BrokerInfo broker{createVaultAndBroker(env, mptAsset, lender)}; + BrokerInfo const broker{createVaultAndBroker(env, mptAsset, lender)}; using namespace loan; @@ -4553,12 +4581,12 @@ protected: auto const pseudoAcct = [&]() { auto const brokerSle = env.le(keylet::loanbroker(broker.brokerID)); if (!BEAST_EXPECT(brokerSle)) - return lender; + return Account{lender}; auto const brokerPseudo = brokerSle->at(sfAccount); return Account("Broker pseudo-account", brokerPseudo); }(); - VerifyLoanStatus verifyLoanStatus(env, broker, pseudoAcct, keylet); + VerifyLoanStatus const verifyLoanStatus(env, broker, pseudoAcct, keylet); auto const originalState = getCurrentState(env, broker, keylet); verifyLoanStatus(originalState); @@ -4609,7 +4637,7 @@ protected: env(payIssuerTx); env.close(); - BrokerInfo broker{createVaultAndBroker(env, iouAsset, lender)}; + BrokerInfo const broker{createVaultAndBroker(env, iouAsset, lender)}; using namespace loan; @@ -4637,12 +4665,12 @@ protected: auto const keylet = keylet::loan(broker.brokerID, loanSequence); createJson = env.json(createJson, sig(sfCounterpartySignature, lender)); - env(createJson, THISLINE); + env(createJson); env.close(); auto loanPayTx = env.json(pay(borrower, keylet.key, STAmount{broker.asset, Number{}})); loanPayTx["Amount"]["value"] = "0.000281284125490196"; - env(loanPayTx, ter(tecINSUFFICIENT_PAYMENT), THISLINE); + env(loanPayTx, ter(tecINSUFFICIENT_PAYMENT)); env.close(); } @@ -4671,7 +4699,7 @@ protected: env(pay(issuer, borrower, iouAsset(1'000))); env.close(); - BrokerInfo broker{createVaultAndBroker(env, iouAsset, lender)}; + BrokerInfo const broker{createVaultAndBroker(env, iouAsset, lender)}; using namespace loan; @@ -4753,7 +4781,7 @@ protected: env(payIssuerTx); env.close(); - BrokerInfo broker{createVaultAndBroker(env, iouAsset, lender)}; + BrokerInfo const broker{createVaultAndBroker(env, iouAsset, lender)}; using namespace loan; @@ -4852,7 +4880,7 @@ protected: env(payIssuerTx); env.close(); - BrokerInfo broker{createVaultAndBroker(env, iouAsset, lender)}; + BrokerInfo const broker{createVaultAndBroker(env, iouAsset, lender)}; { auto const coverDepositValue = broker.asset(broker.params.coverDeposit * 10).value(); env(loanBroker::coverDeposit(lender, broker.brokerID, coverDepositValue)); @@ -5140,7 +5168,7 @@ protected: // pay all but the last payment { - NumberRoundModeGuard mg{Number::upward}; + NumberRoundModeGuard const mg{Number::upward}; Number const payment = beforeState.periodicPayment * (total - 1); XRPAmount const payFee{baseFee * ((total - 1) / loanPaymentsPerFeeIncrement + 1)}; STAmount const paymentAmount = @@ -5244,7 +5272,7 @@ protected: env(pay(issuer, alice, asset(100))); env.close(); - Vault vault{env}; + Vault const vault{env}; auto const [createTx, vaultKeylet] = vault.create({.owner = alice, .asset = asset}); env(createTx); env.close(); @@ -5343,7 +5371,7 @@ protected: auto const loanSetFee = fee(env.current()->fees().base * 2); auto const brokerPreLoan = env.le(keylet::loanbroker(broker.brokerID)); - if (!BEAST_EXPECT(brokerPreLoan)) + if (BEAST_EXPECT(brokerPreLoan); !brokerPreLoan.has_value()) return; auto const loanSequence = brokerPreLoan->at(sfLoanSequence); @@ -5580,7 +5608,7 @@ protected: env.close(); PrettyAsset const asset{xrpIssue(), 1'000'000}; - BrokerParameters brokerParams{}; + BrokerParameters const brokerParams{}; auto const broker = createVaultAndBroker(env, asset, lender, brokerParams); // Create a 3-payment loan so full-payment path is enabled after 1 @@ -5777,10 +5805,10 @@ protected: Env env(*this, all); // Setup: Create accounts - Account issuer{"issuer"}; - Account lender{"lender"}; - Account borrower{"borrower"}; - Account victim{"victim"}; + Account const issuer{"issuer"}; + Account const lender{"lender"}; + Account const borrower{"borrower"}; + Account const victim{"victim"}; env.fund(XRP(1'000'000'00), issuer, lender, borrower, victim); env.close(); @@ -5795,7 +5823,7 @@ protected: env(pay(issuer, victim, asset(50000))); env.close(); - BrokerParameters brokerParams{ + BrokerParameters const brokerParams{ .vaultDeposit = 10000, .debtMax = Number{0}, .coverRateMin = TenthBips32{1000}, @@ -5823,8 +5851,8 @@ protected: { auto const vaultSle = env.le(vaultKeylet); - Number assetsTotal = vaultSle->at(sfAssetsTotal); - Number assetsAvail = vaultSle->at(sfAssetsAvailable); + Number const assetsTotal = vaultSle->at(sfAssetsTotal); + Number const assetsAvail = vaultSle->at(sfAssetsAvailable); log << "Before loan creation:" << std::endl; log << " AssetsTotal: " << assetsTotal << std::endl; @@ -5856,8 +5884,8 @@ protected: { auto const vaultSle = env.le(vaultKeylet); - Number assetsTotal = vaultSle->at(sfAssetsTotal); - Number assetsAvail = vaultSle->at(sfAssetsAvailable); + Number const assetsTotal = vaultSle->at(sfAssetsTotal); + Number const assetsAvail = vaultSle->at(sfAssetsAvailable); log << "After loan creation:" << std::endl; log << " AssetsTotal: " << assetsTotal << std::endl; @@ -5894,8 +5922,8 @@ protected: // Step 8: Verify phantom assets created { auto const vaultSle2 = env.le(vaultKeylet); - Number assetsTotal2 = vaultSle2->at(sfAssetsTotal); - Number assetsAvail2 = vaultSle2->at(sfAssetsAvailable); + Number const assetsTotal2 = vaultSle2->at(sfAssetsTotal); + Number const assetsAvail2 = vaultSle2->at(sfAssetsAvailable); log << "After default:" << std::endl; log << " AssetsTotal: " << assetsTotal2 << std::endl; @@ -5940,7 +5968,7 @@ protected: auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); - if (!BEAST_EXPECT(loanResult)) + if (BEAST_EXPECT(loanResult); !loanResult.has_value()) return; auto broker = std::get(*loanResult); @@ -6011,14 +6039,14 @@ protected: auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); - if (!BEAST_EXPECT(loanResult)) + if (BEAST_EXPECT(loanResult); !loanResult.has_value()) return; auto broker = std::get(*loanResult); auto loanKeylet = std::get(*loanResult); auto pseudoAcct = std::get(*loanResult); - VerifyLoanStatus verifyLoanStatus(env, broker, pseudoAcct, loanKeylet); + VerifyLoanStatus const verifyLoanStatus(env, broker, pseudoAcct, loanKeylet); if (auto const brokerSle = env.le(broker.brokerKeylet()); BEAST_EXPECT(brokerSle)) { @@ -6062,7 +6090,7 @@ protected: auto const txfee = fee(XRP(100)); Env env(*this); - Vault vault(env); + Vault const vault(env); env.fund(XRP(10'000), lender, issuer, borrower, depositor); env.close(); @@ -6122,7 +6150,7 @@ protected: env.close(); // Vault with XRP asset - Vault vault{env}; + Vault const vault{env}; auto [vaultCreate, vaultKeylet] = vault.create({.owner = lender, .asset = xrpIssue()}); env(vaultCreate); env.close(); @@ -6217,14 +6245,14 @@ protected: auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); - if (!BEAST_EXPECT(loanResult)) + if (BEAST_EXPECT(loanResult); !loanResult.has_value()) return; auto broker = std::get(*loanResult); auto loanKeylet = std::get(*loanResult); auto pseudoAcct = std::get(*loanResult); - VerifyLoanStatus verifyLoanStatus(env, broker, pseudoAcct, loanKeylet); + VerifyLoanStatus const verifyLoanStatus(env, broker, pseudoAcct, loanKeylet); makeLoanPayments( env, @@ -6251,12 +6279,16 @@ protected: auto testLoanAsset = [&](auto&& getMaxDebt, auto const& borrower) { Env env(*this); - Vault vault(env); + Vault const vault(env); if (borrower == broker) + { env.fund(XRP(10'000), broker, issuer, depositor); + } else + { env.fund(XRP(10'000), broker, borrower, issuer, depositor); + } env.close(); auto const xrpFee = XRP(100); @@ -6340,7 +6372,7 @@ protected: borrowerAcct); testLoanAsset( [&](Env& env) -> STAmount { - MPTTester mpt( + MPTTester const mpt( {.env = env, .issuer = issuer, .holders = {broker, depositor}, @@ -6377,14 +6409,14 @@ protected: auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, issuer); - if (!BEAST_EXPECT(loanResult)) + if (BEAST_EXPECT(loanResult); !loanResult.has_value()) return; auto broker = std::get(*loanResult); auto loanKeylet = std::get(*loanResult); auto pseudoAcct = std::get(*loanResult); - VerifyLoanStatus verifyLoanStatus(env, broker, pseudoAcct, loanKeylet); + VerifyLoanStatus const verifyLoanStatus(env, broker, pseudoAcct, loanKeylet); makeLoanPayments( env, @@ -6433,14 +6465,14 @@ protected: auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); - if (!BEAST_EXPECT(loanResult)) + if (BEAST_EXPECT(loanResult); !loanResult.has_value()) return; auto broker = std::get(*loanResult); auto loanKeylet = std::get(*loanResult); auto pseudoAcct = std::get(*loanResult); - VerifyLoanStatus verifyLoanStatus(env, broker, pseudoAcct, loanKeylet); + VerifyLoanStatus const verifyLoanStatus(env, broker, pseudoAcct, loanKeylet); auto const state = getCurrentState(env, broker, loanKeylet); @@ -6451,7 +6483,7 @@ protected: tfLoanOverpayment)); env.close(); - PaymentParameters paymentParams{ + PaymentParameters const paymentParams{ .showStepBalances = false, .validateBalances = true, }; @@ -7058,20 +7090,20 @@ protected: Account const borrower("borrower"); // Determine all the random parameters at once - AssetType assetType = static_cast(assetDist(engine_)); + AssetType const assetType = static_cast(assetDist(engine_)); auto const principalRequest = principalDist(engine_); - TenthBips16 managementFeeRate{managementFeeRateDist(engine_)}; + TenthBips16 const managementFeeRate{managementFeeRateDist(engine_)}; auto const serviceFee = serviceFeeDist(engine_); TenthBips32 interest{interestRateDist(engine_)}; auto const payTotal = paymentTotalDist(engine_); auto const payInterval = paymentIntervalDist(engine_); - BrokerParameters brokerParams{ + BrokerParameters const brokerParams{ .vaultDeposit = principalRequest * 10, .debtMax = 0, .coverRateMin = TenthBips32{0}, .managementFeeRate = managementFeeRate}; - LoanParameters loanParams{ + LoanParameters const loanParams{ .account = lender, .counter = borrower, .principalRequest = principalRequest, @@ -7089,12 +7121,12 @@ public: run() override { auto const numIterations = [s = arg()]() -> int { - int defaultNum = 5; + int const defaultNum = 5; if (s.empty()) return defaultNum; try { - std::size_t pos; + std::size_t pos = 0; auto const r = stoi(s, &pos); if (pos != s.size()) return defaultNum; diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index 5a46331aa9..b7f1d22aa7 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -827,7 +828,7 @@ class MPToken_test : public beast::unit_test::suite env.fund(XRP(1'000), alice); env.fund(XRP(1'000), bob); - STAmount mpt{MPTIssue{makeMptID(1, alice)}, UINT64_C(100)}; + STAmount const mpt{MPTIssue{makeMptID(1, alice)}, UINT64_C(100)}; env(pay(alice, bob, mpt), ter(temDISABLED)); } @@ -841,7 +842,7 @@ class MPToken_test : public beast::unit_test::suite env.fund(XRP(1'000), alice); env.fund(XRP(1'000), carol); - STAmount mpt{MPTIssue{makeMptID(1, alice)}, UINT64_C(100)}; + STAmount const mpt{MPTIssue{makeMptID(1, alice)}, UINT64_C(100)}; Json::Value jv; jv[jss::secret] = alice.name(); @@ -1457,7 +1458,7 @@ class MPToken_test : public beast::unit_test::suite { Env env{*this, features}; env.fund(XRP(1'000), alice, bob); - STAmount mpt{MPTIssue{makeMptID(1, alice)}, UINT64_C(100)}; + STAmount const mpt{MPTIssue{makeMptID(1, alice)}, UINT64_C(100)}; Json::Value jv; jv[jss::secret] = alice.name(); jv[jss::tx_json] = pay(alice, bob, mpt); @@ -1805,7 +1806,7 @@ class MPToken_test : public beast::unit_test::suite Account const alice("alice"); auto const USD = alice["USD"]; Account const carol("carol"); - MPTIssue issue(makeMptID(1, alice)); + MPTIssue const issue(makeMptID(1, alice)); STAmount mpt{issue, UINT64_C(100)}; auto const jvb = bridge(alice, USD, alice, USD); for (auto const& feature : {features, features - featureMPTokensV1}) @@ -1840,11 +1841,17 @@ class MPToken_test : public beast::unit_test::suite if (withAmount) jv[field.fieldName] = USD(10).value().getJson(JsonOptions::none); if (field == sfAsset) + { jv[jss::Asset] = to_json(mpt.get()); + } else if (field == sfAsset2) + { jv[jss::Asset2] = to_json(mpt.get()); + } else + { jv[field.fieldName] = mpt.getJson(JsonOptions::none); + } }; // All transactions with sfAmount, which don't support MPT. // Transactions with amount fields, which can't be MPT. @@ -2870,7 +2877,7 @@ class MPToken_test : public beast::unit_test::suite mptAlice.create( {.metadata = "test", .ownerCount = 1, .mutableFlags = tmfMPTCanMutateMetadata}); - std::vector metadatas = { + std::vector const metadatas = { "mutate metadata", "mutate metadata 2", "mutate metadata 3", @@ -3266,6 +3273,123 @@ class MPToken_test : public beast::unit_test::suite mptAlice.claw(alice, bob, 1, tecNO_PERMISSION); } + void + testMultiSendMaximumAmount(FeatureBitset features) + { + // Verify that directSendNoLimitMultiMPT correctly enforces MaximumAmount + // when the issuer sends to multiple receivers. Pre-fixSecurity3_1_3, + // a stale view.read() snapshot caused per-iteration checks to miss + // aggregate overflows. Post-fix, a running total is used instead. + testcase("Multi-send MaximumAmount enforcement"); + + using namespace test::jtx; + + Account const issuer("issuer"); + Account const alice("alice"); + Account const bob("bob"); + + std::uint64_t constexpr maxAmt = 150; + Env env{*this, features}; + + MPTTester mptt(env, issuer, {.holders = {alice, bob}}); + mptt.create({.maxAmt = maxAmt, .ownerCount = 1, .flags = tfMPTCanTransfer}); + mptt.authorize({.account = alice}); + mptt.authorize({.account = bob}); + + Asset const asset{MPTIssue{mptt.issuanceID()}}; + + // Each test case creates a fresh ApplyView and calls + // accountSendMulti from the issuer to the given receivers. + auto const runTest = [&](MultiplePaymentDestinations const& receivers, + TER expectedTer, + std::optional expectedOutstanding, + std::string const& label) { + ApplyViewImpl av(&*env.current(), tapNONE); + auto const ter = + accountSendMulti(av, issuer.id(), asset, receivers, env.app().getJournal("View")); + BEAST_EXPECTS(ter == expectedTer, label); + + // Only verify OutstandingAmount on success — on error the + // view may contain partial state and must be discarded. + if (expectedOutstanding) + { + auto const sle = av.peek(keylet::mptIssuance(mptt.issuanceID())); + if (!BEAST_EXPECT(sle)) + return; + BEAST_EXPECTS(sle->getFieldU64(sfOutstandingAmount) == *expectedOutstanding, label); + } + }; + + using R = MultiplePaymentDestinations; + + // Post-amendment: aggregate check with running total + runTest( + R{{alice.id(), 100}, {bob.id(), 100}}, + tecPATH_DRY, + std::nullopt, + "aggregate exceeds max"); + + runTest(R{{alice.id(), 75}, {bob.id(), 75}}, tesSUCCESS, maxAmt, "aggregate at boundary"); + + runTest(R{{alice.id(), 50}, {bob.id(), 50}}, tesSUCCESS, 100, "aggregate within limit"); + + runTest( + R{{alice.id(), 150}, {bob.id(), 0}}, + tesSUCCESS, + maxAmt, + "one receiver at max, other zero"); + + runTest( + R{{alice.id(), 151}, {bob.id(), 0}}, + tecPATH_DRY, + std::nullopt, + "one receiver exceeds max, other zero"); + + // Issue 50 tokens so outstandingAmount is nonzero, then verify + // the third condition: outstandingAmount > maximumAmount - sendAmount - totalSendAmount + mptt.pay(issuer, alice, 50); + env.close(); + + // maxAmt=150, outstanding=50, so 100 more available + runTest( + R{{alice.id(), 50}, {bob.id(), 50}}, + tesSUCCESS, + maxAmt, + "nonzero outstanding, aggregate at boundary"); + + runTest( + R{{alice.id(), 50}, {bob.id(), 51}}, + tecPATH_DRY, + std::nullopt, + "nonzero outstanding, aggregate exceeds max"); + + runTest( + R{{alice.id(), 100}, {bob.id(), 0}}, + tesSUCCESS, + maxAmt, + "nonzero outstanding, single send at remaining capacity"); + + runTest( + R{{alice.id(), 101}, {bob.id(), 0}}, + tecPATH_DRY, + std::nullopt, + "nonzero outstanding, single send exceeds remaining capacity"); + + // Pre-amendment: the stale per-iteration check allows each + // individual send (100 <= 150) even though the aggregate (200) + // exceeds MaximumAmount. Preserved for ledger replay. + { + // KNOWN BUG (pre-fixSecurity3_1_3): preserved for ledger replay only + env.disableFeature(fixSecurity3_1_3); + runTest( + R{{alice.id(), 100}, {bob.id(), 100}}, + tesSUCCESS, + 250, + "pre-amendment allows over-send"); + env.enableFeature(fixSecurity3_1_3); + } + } + public: void run() override @@ -3273,6 +3397,7 @@ public: using namespace test::jtx; FeatureBitset const all{testable_amendments()}; + testMultiSendMaximumAmount(all); // MPTokenIssuanceCreate testCreateValidation(all - featureSingleAssetVault); testCreateValidation(all - featurePermissionedDomains); diff --git a/src/test/app/Manifest_test.cpp b/src/test/app/Manifest_test.cpp index d9ecf89b26..c2f789e80c 100644 --- a/src/test/app/Manifest_test.cpp +++ b/src/test/app/Manifest_test.cpp @@ -1,4 +1,5 @@ #include +#include #include @@ -86,7 +87,7 @@ public: } } - std::string + static std::string makeManifestString( PublicKey const& pk, SecretKey const& sk, @@ -99,7 +100,10 @@ public: st[sfPublicKey] = pk; st[sfSigningPubKey] = spk; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) sign(st, HashPrefix::manifest, *publicKeyType(spk), ssk); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) sign(st, HashPrefix::manifest, *publicKeyType(pk), sk, sfMasterSignature); Serializer s; @@ -143,9 +147,8 @@ public: Serializer s; st.add(s); - // m is non-const so it can be moved from - std::string m(static_cast(s.data()), s.size()); - if (auto r = deserializeManifest(std::move(m))) + std::string const m(static_cast(s.data()), s.size()); + if (auto r = deserializeManifest(m)) return std::move(*r); Throw("Could not create a revocation manifest"); return *deserializeManifest(std::string{}); // Silence compiler warning. @@ -178,15 +181,14 @@ public: Serializer s; st.add(s); - std::string m(static_cast(s.data()), - s.size()); // non-const so can be moved - if (auto r = deserializeManifest(std::move(m))) + std::string const m(static_cast(s.data()), s.size()); + if (auto r = deserializeManifest(m)) return std::move(*r); Throw("Could not create a manifest"); return *deserializeManifest(std::string{}); // Silence compiler warning. } - Manifest + static Manifest clone(Manifest const& m) { Manifest m2(m.serialized, m.masterKey, m.signingKey, m.sequence, m.domain); @@ -247,7 +249,9 @@ public: { // save should store all trusted master keys to db std::vector s1; - std::vector keys; + std::vector const keys; + s1.reserve(inManifests.size()); + for (auto const& man : inManifests) s1.push_back(toBase58(TokenType::NodePublic, man->masterKey)); unl->load({}, s1, keys); @@ -347,7 +351,9 @@ public: ss.add32(HashPrefix::manifest); st.addWithoutSigningFields(ss); auto const sig = sign(KeyType::secp256k1, kp.second, ss.slice()); - BEAST_EXPECT(strHex(sig) == strHex(*m.getSignature())); + BEAST_EXPECT( + strHex(sig) == + strHex(*m.getSignature())); // NOLINT(bugprone-unchecked-optional-access) auto const masterSig = sign(KeyType::ed25519, sk, ss.slice()); BEAST_EXPECT(strHex(masterSig) == strHex(m.getMasterSignature())); @@ -446,7 +452,9 @@ public: auto const token = loadValidatorToken(tokenBlob); BEAST_EXPECT(token); - BEAST_EXPECT(token->validationSecret == *valSecret); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + BEAST_EXPECT(test::equal(token->validationSecret, *valSecret)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(token->manifest == manifest); } { @@ -574,12 +582,14 @@ public: auto const manifest = deserializeManifest(m); BEAST_EXPECT(manifest); + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(manifest->masterKey == pk); BEAST_EXPECT(manifest->signingKey == spk); BEAST_EXPECT(manifest->sequence == sequence); BEAST_EXPECT(manifest->serialized == m); BEAST_EXPECT(manifest->domain.empty()); BEAST_EXPECT(manifest->verify()); + // NOLINTEND(bugprone-unchecked-optional-access) } { // invalid manifest (empty domain) @@ -593,12 +603,12 @@ public: BEAST_EXPECT(!deserializeManifest(toString(st))); } { // invalid manifest (domain too long) - std::string s(254, 'a'); + std::string const s(254, 'a'); auto const st = buildManifestObject(++sequence, s + ".example.com"); BEAST_EXPECT(!deserializeManifest(toString(st))); } { // invalid manifest (domain component too long) - std::string s(72, 'a'); + std::string const s(72, 'a'); auto const st = buildManifestObject(++sequence, s + ".example.com"); BEAST_EXPECT(!deserializeManifest(toString(st))); } @@ -611,12 +621,14 @@ public: auto const manifest = deserializeManifest(m); BEAST_EXPECT(manifest); + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(manifest->masterKey == pk); BEAST_EXPECT(manifest->signingKey == spk); BEAST_EXPECT(manifest->sequence == sequence); BEAST_EXPECT(manifest->serialized == m); BEAST_EXPECT(manifest->domain == "example.com"); BEAST_EXPECT(manifest->verify()); + // NOLINTEND(bugprone-unchecked-optional-access) } { // valid manifest with invalid signature @@ -627,12 +639,14 @@ public: auto const manifest = deserializeManifest(m); BEAST_EXPECT(manifest); + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(manifest->masterKey == pk); BEAST_EXPECT(manifest->signingKey == spk); BEAST_EXPECT(manifest->sequence == sequence + 1); BEAST_EXPECT(manifest->serialized == m); BEAST_EXPECT(manifest->domain == "example.com"); BEAST_EXPECT(!manifest->verify()); + // NOLINTEND(bugprone-unchecked-optional-access) } { // reject missing sequence @@ -716,15 +730,16 @@ public: auto const manifest = deserializeManifest(m); BEAST_EXPECT(manifest); + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(manifest->masterKey == pk); - // Since this manifest is revoked, it should not have - // a signingKey + // Since this manifest is revoked, it should not have a signingKey BEAST_EXPECT(!manifest->signingKey); BEAST_EXPECT(manifest->revoked()); BEAST_EXPECT(manifest->domain.empty()); BEAST_EXPECT(manifest->serialized == m); BEAST_EXPECT(manifest->verify()); + // NOLINTEND(bugprone-unchecked-optional-access) } { // can't specify an ephemeral signing key diff --git a/src/test/app/MultiSign_test.cpp b/src/test/app/MultiSign_test.cpp index adfcb12ee0..7a5a49ca9b 100644 --- a/src/test/app/MultiSign_test.cpp +++ b/src/test/app/MultiSign_test.cpp @@ -63,7 +63,7 @@ public: { // Attach a signer list to alice. Should fail. - Json::Value signersList = signers(alice, 1, {{bogie, 1}}); + Json::Value const signersList = signers(alice, 1, {{bogie, 1}}); env(signersList, ter(tecINSUFFICIENT_RESERVE)); env.close(); env.require(owners(alice, 0)); @@ -81,7 +81,7 @@ public: env(pay(env.master, alice, fee - drops(1))); // Replace with the biggest possible signer list. Should fail. - Json::Value bigSigners = signers( + Json::Value const bigSigners = signers( alice, 1, {{bogie, 1}, @@ -163,25 +163,20 @@ public: {spook, 1}}), ter(temBAD_QUORUM)); - // clang-format off // Make a signer list that's too big. Should fail. Account const spare("spare", KeyType::secp256k1); env(signers( alice, 1, - std::vector{{bogie, 1}, {demon, 1}, {ghost, 1}, - {haunt, 1}, {jinni, 1}, {phase, 1}, - {shade, 1}, {spook, 1}, {spare, 1}, - {acc10, 1}, {acc11, 1}, {acc12, 1}, - {acc13, 1}, {acc14, 1}, {acc15, 1}, - {acc16, 1}, {acc17, 1}, {acc18, 1}, - {acc19, 1}, {acc20, 1}, {acc21, 1}, - {acc22, 1}, {acc23, 1}, {acc24, 1}, - {acc25, 1}, {acc26, 1}, {acc27, 1}, - {acc28, 1}, {acc29, 1}, {acc30, 1}, - {acc31, 1}, {acc32, 1}, {acc33, 1}}), + std::vector{ + {bogie, 1}, {demon, 1}, {ghost, 1}, {haunt, 1}, {jinni, 1}, {phase, 1}, + {shade, 1}, {spook, 1}, {spare, 1}, {acc10, 1}, {acc11, 1}, {acc12, 1}, + {acc13, 1}, {acc14, 1}, {acc15, 1}, {acc16, 1}, {acc17, 1}, {acc18, 1}, + {acc19, 1}, {acc20, 1}, {acc21, 1}, {acc22, 1}, {acc23, 1}, {acc24, 1}, + {acc25, 1}, {acc26, 1}, {acc27, 1}, {acc28, 1}, {acc29, 1}, {acc30, 1}, + {acc31, 1}, {acc32, 1}, {acc33, 1}, + }), ter(temMALFORMED)); - // clang-format on env.close(); env.require(owners(alice, 0)); } @@ -486,7 +481,7 @@ public: env.close(); auto const baseFee = env.current()->fees().base; - std::uint32_t aliceSeq; + std::uint32_t aliceSeq = 0; // these represent oft-repeated setup for input json below auto setup_tx = [&]() -> Json::Value { @@ -1017,7 +1012,7 @@ public: auto const baseFee = env.current()->fees().base; { // Single-sign, but leave an empty SigningPubKey. - JTx tx = env.jt(noop(alice), sig(alice)); + JTx const tx = env.jt(noop(alice), sig(alice)); STTx local = *(tx.stx); local.setFieldVL(sfSigningPubKey, Blob()); // Empty SigningPubKey auto const info = submitSTTx(local); @@ -1027,7 +1022,7 @@ public: } { // Single-sign, but invalidate the signature. - JTx tx = env.jt(noop(alice), sig(alice)); + JTx const tx = env.jt(noop(alice), sig(alice)); STTx local = *(tx.stx); // Flip some bits in the signature. auto badSig = local.getFieldVL(sfTxnSignature); @@ -1041,7 +1036,7 @@ public: } { // Single-sign, but invalidate the sequence number. - JTx tx = env.jt(noop(alice), sig(alice)); + JTx const tx = env.jt(noop(alice), sig(alice)); STTx local = *(tx.stx); // Flip some bits in the signature. auto seq = local.getFieldU32(sfSequence); @@ -1054,7 +1049,7 @@ public: } { // Multisign, but leave a nonempty sfSigningPubKey. - JTx tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie)); + JTx const tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie)); STTx local = *(tx.stx); local[sfSigningPubKey] = alice.pk(); // Insert sfSigningPubKey auto const info = submitSTTx(local); @@ -1064,7 +1059,7 @@ public: } { // Both multi- and single-sign with an empty SigningPubKey. - JTx tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie)); + JTx const tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie)); STTx local = *(tx.stx); local.sign(alice.pk(), alice.sk()); local.setFieldVL(sfSigningPubKey, Blob()); // Empty SigningPubKey @@ -1075,7 +1070,7 @@ public: } { // Multisign but invalidate one of the signatures. - JTx tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie)); + JTx const tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie)); STTx local = *(tx.stx); // Flip some bits in the signature. auto& signer = local.peekFieldArray(sfSigners).back(); @@ -1090,7 +1085,7 @@ public: } { // Multisign with an empty signers array should fail. - JTx tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie)); + JTx const tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie)); STTx local = *(tx.stx); local.peekFieldArray(sfSigners).clear(); // Empty Signers array. auto const info = submitSTTx(local); @@ -1099,7 +1094,7 @@ public: "fails local checks: Invalid Signers array size."); } { - JTx tx = env.jt( + JTx const tx = env.jt( noop(alice), fee(2 * baseFee), @@ -1137,7 +1132,7 @@ public: bogie, bogie, bogie)); - STTx local = *(tx.stx); + STTx const local = *(tx.stx); auto const info = submitSTTx(local); BEAST_EXPECT( info[jss::result][jss::error_exception] == @@ -1145,8 +1140,8 @@ public: } { // The account owner may not multisign for themselves. - JTx tx = env.jt(noop(alice), fee(2 * baseFee), msig(alice)); - STTx local = *(tx.stx); + JTx const tx = env.jt(noop(alice), fee(2 * baseFee), msig(alice)); + STTx const local = *(tx.stx); auto const info = submitSTTx(local); BEAST_EXPECT( info[jss::result][jss::error_exception] == @@ -1154,8 +1149,8 @@ public: } { // No duplicate multisignatures allowed. - JTx tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie, bogie)); - STTx local = *(tx.stx); + JTx const tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie, bogie)); + STTx const local = *(tx.stx); auto const info = submitSTTx(local); BEAST_EXPECT( info[jss::result][jss::error_exception] == @@ -1163,7 +1158,7 @@ public: } { // Multisignatures must be submitted in sorted order. - JTx tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie, demon)); + JTx const tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie, demon)); STTx local = *(tx.stx); // Unsort the Signers array. auto& signers = local.peekFieldArray(sfSigners); diff --git a/src/test/app/NFTokenAuth_test.cpp b/src/test/app/NFTokenAuth_test.cpp index b973530f41..0c044fc009 100644 --- a/src/test/app/NFTokenAuth_test.cpp +++ b/src/test/app/NFTokenAuth_test.cpp @@ -1,12 +1,12 @@ #include -#include +#include namespace xrpl { class NFTokenAuth_test : public beast::unit_test::suite { - auto + static auto mintAndOfferNFT( test::jtx::Env& env, test::jtx::Account const& account, @@ -33,9 +33,9 @@ public: using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; - Account A2{"A2"}; + Account const G1{"G1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; auto const USD{G1["USD"]}; env.fund(XRP(10000), G1, A1, A2); @@ -88,7 +88,7 @@ public: Env env(*this, features); Account G1{"G1"}; Account A1{"A1"}; - Account A2{"A2"}; + Account const A2{"A2"}; auto const USD{G1["USD"]}; env.fund(XRP(10000), G1, A1, A2); @@ -110,7 +110,7 @@ public: view.rawInsert(sleA1); return true; }; - env.app().openLedger().modify(unauthTrustline); + env.app().getOpenLedger().modify(unauthTrustline); if (features[fixEnforceNFTokenTrustlineV2]) { @@ -134,7 +134,7 @@ public: Env env(*this, features); Account G1{"G1"}; Account A1{"A1"}; - Account A2{"A2"}; + Account const A2{"A2"}; auto const USD{G1["USD"]}; env.fund(XRP(10000), G1, A1, A2); @@ -173,7 +173,7 @@ public: view.rawInsert(sleA1); return true; }; - env.app().openLedger().modify(unauthTrustline); + env.app().getOpenLedger().modify(unauthTrustline); if (features[fixEnforceNFTokenTrustlineV2]) { // test: check that offer can't be accepted even with balance @@ -190,9 +190,9 @@ public: using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; - Account A2{"A2"}; + Account const G1{"G1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; auto const USD{G1["USD"]}; env.fund(XRP(10000), G1, A1, A2); @@ -267,7 +267,7 @@ public: Env env(*this, features); Account G1{"G1"}; Account A1{"A1"}; - Account A2{"A2"}; + Account const A2{"A2"}; auto const USD{G1["USD"]}; env.fund(XRP(10000), G1, A1, A2); @@ -293,7 +293,7 @@ public: view.rawInsert(sleA1); return true; }; - env.app().openLedger().modify(unauthTrustline); + env.app().getOpenLedger().modify(unauthTrustline); if (features[fixEnforceNFTokenTrustlineV2]) { env(token::acceptSellOffer(A1, sellIdx), ter(tecNO_AUTH)); @@ -307,10 +307,10 @@ public: using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; - Account A2{"A2"}; - Account broker{"broker"}; + Account const G1{"G1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const broker{"broker"}; auto const USD{G1["USD"]}; env.fund(XRP(10000), G1, A1, A2, broker); @@ -375,8 +375,8 @@ public: Env env(*this, features); Account G1{"G1"}; Account A1{"A1"}; - Account A2{"A2"}; - Account broker{"broker"}; + Account const A2{"A2"}; + Account const broker{"broker"}; auto const USD{G1["USD"]}; env.fund(XRP(10000), G1, A1, A2, broker); @@ -412,7 +412,7 @@ public: view.rawInsert(sleA1); return true; }; - env.app().openLedger().modify(unauthTrustline); + env.app().getOpenLedger().modify(unauthTrustline); if (features[fixEnforceNFTokenTrustlineV2]) { @@ -433,10 +433,10 @@ public: using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account A1{"A1"}; - Account A2{"A2"}; - Account broker{"broker"}; + Account const G1{"G1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const broker{"broker"}; auto const USD{G1["USD"]}; env.fund(XRP(10000), G1, A1, A2, broker); @@ -507,10 +507,10 @@ public: using namespace test::jtx; Env env(*this, features); - Account G1{"G1"}; - Account minter{"minter"}; - Account A1{"A1"}; - Account A2{"A2"}; + Account const G1{"G1"}; + Account const minter{"minter"}; + Account const A1{"A1"}; + Account const A2{"A2"}; auto const USD{G1["USD"]}; env.fund(XRP(10000), G1, minter, A1, A2); diff --git a/src/test/app/NFTokenBurn_test.cpp b/src/test/app/NFTokenBurn_test.cpp index 8cd67bd72b..99b1832466 100644 --- a/src/test/app/NFTokenBurn_test.cpp +++ b/src/test/app/NFTokenBurn_test.cpp @@ -1,8 +1,9 @@ #include +#include #include #include -#include +#include #include @@ -23,7 +24,7 @@ class NFTokenBurn_test : public beast::unit_test::suite // Helper function that returns new nft id for an account and create // specified number of sell offers - uint256 + static uint256 createNftAndOffers( test::jtx::Env& env, test::jtx::Account const& owner, @@ -59,7 +60,7 @@ class NFTokenBurn_test : public beast::unit_test::suite noisy = true, }; - void + static void printNFTPages(test::jtx::Env& env, Volume vol) { Json::Value jvParams; @@ -85,7 +86,7 @@ class NFTokenBurn_test : public beast::unit_test::suite if (state[i].isMember(sfNFTokens.jsonName) && state[i][sfNFTokens.jsonName].isArray()) { - std::uint32_t tokenCount = state[i][sfNFTokens.jsonName].size(); + std::uint32_t const tokenCount = state[i][sfNFTokens.jsonName].size(); std::cout << tokenCount << " NFtokens in page " << state[i][jss::index].asString() << std::endl; @@ -96,14 +97,18 @@ class NFTokenBurn_test : public beast::unit_test::suite else { if (tokenCount > 0) + { std::cout << "first: " << state[i][sfNFTokens.jsonName][0u].toStyledString() << std::endl; + } if (tokenCount > 1) + { std::cout << "last: " << state[i][sfNFTokens.jsonName][tokenCount - 1].toStyledString() << std::endl; + } } } } @@ -197,7 +202,7 @@ class NFTokenBurn_test : public beast::unit_test::suite { // We do the same work on alice and minter, so make a lambda. auto xferNFT = [&env, &becky](AcctStat& acct, auto& iter) { - uint256 offerIndex = keylet::nftoffer(acct.acct, env.seq(acct.acct)).key; + uint256 const offerIndex = keylet::nftoffer(acct.acct, env.seq(acct.acct)).key; env(token::createOffer(acct, *iter, XRP(0)), txflags(tfSellNFToken)); env.close(); env(token::acceptSellOffer(becky, offerIndex)); @@ -221,7 +226,7 @@ class NFTokenBurn_test : public beast::unit_test::suite // Next we'll create offers for all of those NFTs. This calls for // another lambda. auto addOffers = [&env](AcctStat& owner, AcctStat& other1, AcctStat& other2) { - for (uint256 nft : owner.nfts) + for (uint256 const nft : owner.nfts) { // Create sell offers for owner. env(token::createOffer(owner, nft, drops(1)), @@ -256,7 +261,7 @@ class NFTokenBurn_test : public beast::unit_test::suite std::uniform_int_distribution acctDist(0, 2); std::uniform_int_distribution mintDist(0, 1); - while (stats[0]->nfts.size() > 0 || stats[1]->nfts.size() > 0 || stats[2]->nfts.size() > 0) + while (!stats[0]->nfts.empty() || !stats[1]->nfts.empty() || !stats[2]->nfts.empty()) { // Pick an account to burn an nft. If there are no nfts left // pick again. @@ -273,14 +278,20 @@ class NFTokenBurn_test : public beast::unit_test::suite // Decide which of the accounts should burn the nft. If the // owner is becky then any of the three accounts can burn. // Otherwise either alice or minter can burn. - AcctStat& burner = owner.acct == becky.acct ? *(stats[acctDist(engine)]) - : mintDist(engine) ? alice - : minter; + AcctStat const& burner = [&]() -> AcctStat& { + if (owner.acct == becky.acct) + return *(stats[acctDist(engine)]); + return mintDist(engine) ? alice : minter; + }(); if (owner.acct == burner.acct) + { env(token::burn(burner, nft)); + } else + { env(token::burn(burner, nft), token::owner(owner)); + } env.close(); // Every time we burn an nft, the number of nfts they hold should @@ -388,7 +399,7 @@ class NFTokenBurn_test : public beast::unit_test::suite // Generate three packed pages. Then burn the tokens in order from // first to last. This exercises specific cases where coalescing // pages is not possible. - std::vector nfts = genPackedTokens(); + std::vector const nfts = genPackedTokens(); BEAST_EXPECT(nftCount(env, alice) == 96); BEAST_EXPECT(ownerCount(env, alice) == 3); @@ -726,9 +737,9 @@ class NFTokenBurn_test : public beast::unit_test::suite // Create an ApplyContext we can use to run the invariant // checks. These variables must outlive the ApplyContext. OpenView ov{*env.current()}; - STTx tx{ttACCOUNT_SET, [](STObject&) {}}; + STTx const tx{ttACCOUNT_SET, [](STObject&) {}}; test::StreamSink sink{beast::severities::kWarning}; - beast::Journal jlog{sink}; + beast::Journal const jlog{sink}; ApplyContext ac{ env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, tapNONE, jlog}; @@ -759,9 +770,9 @@ class NFTokenBurn_test : public beast::unit_test::suite // Create an ApplyContext we can use to run the invariant // checks. These variables must outlive the ApplyContext. OpenView ov{*env.current()}; - STTx tx{ttACCOUNT_SET, [](STObject&) {}}; + STTx const tx{ttACCOUNT_SET, [](STObject&) {}}; test::StreamSink sink{beast::severities::kWarning}; - beast::Journal jlog{sink}; + beast::Journal const jlog{sink}; ApplyContext ac{ env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, tapNONE, jlog}; @@ -1104,7 +1115,7 @@ class NFTokenBurn_test : public beast::unit_test::suite env.close(); // minter sells the last 32 NFTs back to alice. - for (uint256 nftID : last32NFTs) + for (uint256 const nftID : last32NFTs) { // minter creates an offer for the NFToken. uint256 const minterOfferIndex = keylet::nftoffer(minter, env.seq(minter)).key; diff --git a/src/test/app/NFTokenDir_test.cpp b/src/test/app/NFTokenDir_test.cpp index 551b987204..6ed5912034 100644 --- a/src/test/app/NFTokenDir_test.cpp +++ b/src/test/app/NFTokenDir_test.cpp @@ -1,9 +1,9 @@ #include +#include #include #include #include -#include #include @@ -20,7 +20,7 @@ class NFTokenDir_test : public beast::unit_test::suite noisy = true, }; - void + static void printNFTPages(test::jtx::Env& env, Volume vol) { Json::Value jvParams; @@ -46,7 +46,7 @@ class NFTokenDir_test : public beast::unit_test::suite if (state[i].isMember(sfNFTokens.jsonName) && state[i][sfNFTokens.jsonName].isArray()) { - std::uint32_t tokenCount = state[i][sfNFTokens.jsonName].size(); + std::uint32_t const tokenCount = state[i][sfNFTokens.jsonName].size(); std::cout << tokenCount << " NFtokens in page " << state[i][jss::index].asString() << std::endl; @@ -57,14 +57,18 @@ class NFTokenDir_test : public beast::unit_test::suite else { if (tokenCount > 0) + { std::cout << "first: " << state[i][sfNFTokens.jsonName][0u].toStyledString() << std::endl; + } if (tokenCount > 1) + { std::cout << "last: " << state[i][sfNFTokens.jsonName][tokenCount - 1].toStyledString() << std::endl; + } } } } @@ -100,7 +104,7 @@ class NFTokenDir_test : public beast::unit_test::suite nftIDs.reserve(nftCount); for (int i = 0; i < nftCount; ++i) { - std::uint32_t taxon = toUInt32(nft::cipheredTaxon(i, nft::toTaxon(0))); + std::uint32_t const taxon = toUInt32(nft::cipheredTaxon(i, nft::toTaxon(0))); nftIDs.emplace_back(token::getNextID(env, issuer, taxon, tfTransferable)); env(token::mint(issuer, taxon), txflags(tfTransferable)); env.close(); @@ -156,7 +160,7 @@ class NFTokenDir_test : public beast::unit_test::suite // Create accounts for all of the seeds and fund those accounts. std::vector accounts; accounts.reserve(seeds.size()); - for (std::string_view seed : seeds) + for (std::string_view const seed : seeds) { Account const& account = accounts.emplace_back(Account::base58Seed, std::string(seed)); @@ -360,7 +364,7 @@ class NFTokenDir_test : public beast::unit_test::suite // Create accounts for all of the seeds and fund those accounts. std::vector accounts; accounts.reserve(seeds.size()); - for (std::string_view seed : seeds) + for (std::string_view const seed : seeds) { Account const& account = accounts.emplace_back(Account::base58Seed, std::string(seed)); @@ -591,7 +595,7 @@ class NFTokenDir_test : public beast::unit_test::suite // Create accounts for all of the seeds and fund those accounts. std::vector accounts; accounts.reserve(seeds.size()); - for (std::string_view seed : seeds) + for (std::string_view const seed : seeds) { Account const& account = accounts.emplace_back(Account::base58Seed, std::string(seed)); env.fund(XRP(10000), account); @@ -754,7 +758,7 @@ class NFTokenDir_test : public beast::unit_test::suite // Create accounts for all of the seeds and fund those accounts. std::vector accounts; accounts.reserve(seeds.size()); - for (std::string_view seed : seeds) + for (std::string_view const seed : seeds) { Account const& account = accounts.emplace_back(Account::base58Seed, std::string(seed)); env.fund(XRP(10000), account); @@ -779,7 +783,7 @@ class NFTokenDir_test : public beast::unit_test::suite for (Account const& account : accounts) { // Mint the NFT. Tweak the taxon so zero is always stored. - std::uint32_t taxon = toUInt32(nft::cipheredTaxon(i, nft::toTaxon(0))); + std::uint32_t const taxon = toUInt32(nft::cipheredTaxon(i, nft::toTaxon(0))); uint256 const& nftID = nftIDsByPage[i].emplace_back( token::getNextID(env, account, taxon, tfTransferable)); @@ -827,7 +831,7 @@ class NFTokenDir_test : public beast::unit_test::suite // buyer accepts all of the offers that won't cause an overflow. // Fill the center and outsides first to exercise different boundary // cases. - for (int i : std::initializer_list{3, 6, 0, 1, 2, 5, 4}) + for (int const i : std::initializer_list{3, 6, 0, 1, 2, 5, 4}) { for (uint256 const& offer : offers[i]) { diff --git a/src/test/app/NFToken_test.cpp b/src/test/app/NFToken_test.cpp index 7ff2e7d1cb..abbd5ba8e1 100644 --- a/src/test/app/NFToken_test.cpp +++ b/src/test/app/NFToken_test.cpp @@ -1,9 +1,9 @@ #include #include +#include #include #include -#include #include @@ -53,7 +53,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite } // Helper function returns the close time of the parent ledger. - std::uint32_t + static std::uint32_t lastClose(test::jtx::Env& env) { return env.current()->header().parentCloseTime.time_since_epoch().count(); @@ -171,7 +171,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite int line) { auto oneCheck = [line, this](char const* type, std::uint32_t found, std::uint32_t exp) { if (found == exp) + { pass(); + } else { std::stringstream ss; @@ -241,7 +243,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite { env(token::burn(alice, token::getID(env, alice, 0, seq++))); env.close(); - checkAliceOwnerMintedBurned((33 - seq) ? 1 : 0, 33, seq, __LINE__); + checkAliceOwnerMintedBurned(((33 - seq) != 0u) ? 1 : 0, 33, seq, __LINE__); } // alice burns a non-existent NFT. @@ -269,7 +271,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite auto oneCheck = [this](char const* type, std::uint32_t found, std::uint32_t exp, int line) { if (found == exp) + { pass(); + } else { std::stringstream ss; @@ -348,7 +352,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite { env(token::burn(minter, token::getID(env, alice, 0, nftSeq++))); env.close(); - checkMintersOwnerMintedBurned(0, 66, nftSeq, (65 - seq) ? 1 : 0, 0, 0, __LINE__); + checkMintersOwnerMintedBurned( + 0, 66, nftSeq, ((65 - seq) != 0u) ? 1 : 0, 0, 0, __LINE__); } // minter has one more NFT to burn. Should take her owner count to @@ -394,7 +399,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Note that we're bypassing almost all of the ledger's safety // checks with this modify() call. If you call close() between // here and the end of the test all the effort will be lost. - env.app().openLedger().modify([&alice](OpenView& view, beast::Journal j) { + env.app().getOpenLedger().modify([&alice](OpenView& view, beast::Journal j) { // Get the account root we want to hijack. auto const sle = view.read(keylet::account(alice.id())); if (!sle) @@ -575,7 +580,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); - uint256 nftNoXferID = token::getNextID(env, alice, 0); + uint256 const nftNoXferID = token::getNextID(env, alice, 0); env(token::mint(alice, 0)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -844,7 +849,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // List of tokens to delete is too long. { - std::vector offers(maxTokenOfferCancelCount + 1, buyerOfferIndex); + std::vector const offers(maxTokenOfferCancelCount + 1, buyerOfferIndex); env(token::cancelOffer(buyer, offers), ter(temMALFORMED)); env.close(); @@ -931,7 +936,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(ownerCount(env, alice) == aliceCount); - uint256 nftNoXferID = token::getNextID(env, alice, 0); + uint256 const nftNoXferID = token::getNextID(env, alice, 0); env(token::mint(alice, 0)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == aliceCount); @@ -1510,7 +1515,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // An nft without flagCreateTrustLines but with a non-zero transfer // fee will not allow creating offers that use IOUs for payment. - for (std::uint32_t xferFee : {0, 1}) + for (std::uint32_t const xferFee : {0, 1}) { uint256 const nftNoAutoTrustID{ token::getNextID(env, alice, 0u, tfTransferable, xferFee)}; @@ -1525,7 +1530,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env.close(); // becky attempts to sell the nft for AUD. - TER const createOfferTER = xferFee ? TER(tecNO_LINE) : TER(tesSUCCESS); + TER const createOfferTER = (xferFee != 0u) ? TER(tecNO_LINE) : TER(tesSUCCESS); uint256 const beckyOfferIndex = keylet::nftoffer(becky, env.seq(becky)).key; env(token::createOffer(becky, nftNoAutoTrustID, gwAUD(100)), txflags(tfSellNFToken), @@ -1547,7 +1552,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // An nft with flagCreateTrustLines but with a non-zero transfer // fee allows transfers using IOUs for payment. { - std::uint16_t transferFee = 10000; // 10% + std::uint16_t const transferFee = 10000; // 10% uint256 const nftAutoTrustID{ token::getNextID(env, alice, 0u, tfTransferable | tfTrustLine, transferFee)}; @@ -1601,7 +1606,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Now that alice has trust lines preestablished, an nft without // flagCreateTrustLines will work for preestablished trust lines. { - std::uint16_t transferFee = 5000; // 5% + std::uint16_t const transferFee = 5000; // 5% uint256 const nftNoAutoTrustID{ token::getNextID(env, alice, 0u, tfTransferable, transferFee)}; env(token::mint(alice, 0u), token::xferFee(transferFee), txflags(tfTransferable)); @@ -2171,9 +2176,13 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite 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); @@ -2252,7 +2261,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env.close(); // Here is the smallest expressible gwXAU amount. - STAmount tinyXAU(gwXAU.issue(), STAmount::cMinValue, STAmount::cMinOffset); + STAmount const tinyXAU(gwXAU.issue(), STAmount::cMinValue, STAmount::cMinOffset); // minter buys the nft for tinyXAU. Since the transfer involves // alice there should be no transfer fee. @@ -2347,7 +2356,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite auto check = [this](std::uint32_t taxon, uint256 const& nftID) { nft::Taxon const gotTaxon = nft::getTaxon(nftID); if (nft::toTaxon(taxon) == gotTaxon) + { pass(); + } else { std::stringstream ss; @@ -2490,7 +2501,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite void testCreateOfferDestination(FeatureBitset features) { - // Explore the CreateOffer Destination field. + // Explore the OfferCreate Destination field. testcase("Create offer destination"); using namespace test::jtx; @@ -2905,7 +2916,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite void testCreateOfferExpiration(FeatureBitset features) { - // Explore the CreateOffer Expiration field. + // Explore the OfferCreate Expiration field. testcase("Create offer expiration"); using namespace test::jtx; @@ -2930,7 +2941,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite uint256 const nftokenID1 = token::getNextID(env, issuer, 0, tfTransferable); env(token::mint(minter, 0), token::issuer(issuer), txflags(tfTransferable)); env.close(); - uint8_t issuerCount, minterCount, buyerCount; + uint8_t issuerCount = 0, minterCount = 0, buyerCount = 0; // Test how adding an Expiration field to an offer affects permissions // for cancelling offers. @@ -3691,7 +3702,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite int line) { for (Account const& acct : accounts) { - if (std::uint32_t ownerCount = test::jtx::ownerCount(env, acct); + if (std::uint32_t const ownerCount = test::jtx::ownerCount(env, acct); ownerCount != 1) { std::stringstream ss; @@ -4522,7 +4533,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite }; // There are no sell offers. - checkOffers("nft_sell_offers", 0, false, __LINE__); + checkOffers("nft_sell_offers", 0, 0, __LINE__); // A lambda that generates sell offers. STAmount sellPrice = XRP(0); @@ -4791,7 +4802,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env(pay(secondarySeller, gw, env.balance(secondarySeller, gwXPB))); auto brokerDiff = gwXAU(5000) - env.balance(broker, gwXAU); if (brokerDiff > gwXAU(0)) + { env(pay(gw, broker, brokerDiff)); + } else if (brokerDiff < gwXAU(0)) { brokerDiff.negate(); @@ -6583,7 +6596,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env.close(); // issuer creates two NFTs: one with and one without AutoTrustLine. - std::uint16_t xferFee = 5000; // 5% + std::uint16_t const xferFee = 5000; // 5% uint256 const nftAutoTrustID{ token::getNextID(env, issuer, 0u, tfTransferable | tfTrustLine, xferFee)}; env(token::mint(issuer, 0u), @@ -6739,7 +6752,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env.close(); // issuer creates two NFTs: one with and one without AutoTrustLine. - std::uint16_t xferFee = 5000; // 5% + std::uint16_t const xferFee = 5000; // 5% uint256 const nftAutoTrustID{ token::getNextID(env, issuer, 0u, tfTransferable | tfTrustLine, xferFee)}; env(token::mint(issuer, 0u), @@ -6976,7 +6989,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite auto checkURI = [&accountNFTs, this](Account const& acct, char const* uri, int line) { auto const nfts = accountNFTs(acct); if (nfts.size() == 1) + { pass(); + } else { std::ostringstream text; @@ -6988,7 +7003,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite if (uri == nullptr) { if (!nfts[0u].isMember(sfURI.jsonName)) + { pass(); + } else { std::ostringstream text; @@ -6999,7 +7016,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite } if (nfts[0u][sfURI.jsonName] == strHex(std::string(uri))) + { pass(); + } else { std::ostringstream text; diff --git a/src/test/app/NetworkID_test.cpp b/src/test/app/NetworkID_test.cpp index a37f8342e1..17245f7ee5 100644 --- a/src/test/app/NetworkID_test.cpp +++ b/src/test/app/NetworkID_test.cpp @@ -18,7 +18,7 @@ public: testNetworkID(); } - std::unique_ptr + static std::unique_ptr makeNetworkConfig(uint32_t networkID) { using namespace jtx; diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp index d22d907322..ad6ec656cb 100644 --- a/src/test/app/Offer_test.cpp +++ b/src/test/app/Offer_test.cpp @@ -11,13 +11,13 @@ namespace test { class OfferBaseUtil_test : public beast::unit_test::suite { - XRPAmount + static XRPAmount reserve(jtx::Env& env, std::uint32_t count) { return env.current()->fees().accountReserve(count); } - std::uint32_t + static std::uint32_t lastClose(jtx::Env& env) { return env.current()->header().parentCloseTime.time_since_epoch().count(); @@ -93,7 +93,7 @@ public: // Offers for the good quality path env(offer(carol, BTC(1), USD(100))); - PathSet paths(Path(XRP, USD), Path(USD)); + PathSet const paths(Path(XRP, USD), Path(USD)); env(pay(alice, bob, USD(100)), json(paths.json()), @@ -382,7 +382,7 @@ public: ter(expectedTer)); env.close(); - if (expectedTer == tesSUCCESS) + if (isTesSuccess(expectedTer)) { env.require(offers(carol, 0)); env.require(balance(carol, @@ -419,7 +419,7 @@ public: auto const EUR = gw["EUR"]; auto tinyAmount = [&](IOU const& iou) -> PrettyAmount { - STAmount amt( + STAmount const amt( iou.issue(), /*mantissa*/ 1, /*exponent*/ -81); @@ -507,7 +507,7 @@ public: ter(expectedTer)); env.close(); - if (expectedTer == tesSUCCESS) + if (isTesSuccess(expectedTer)) { env.require(offers(carol, 0)); env.require(balance(carol, @@ -863,7 +863,7 @@ public: auto const aliceOffers = offersOnAccount(env, alice); BEAST_EXPECT(aliceOffers.size() == 1); - for (auto offerPtr : aliceOffers) + for (auto const& offerPtr : aliceOffers) { auto const& offer = *offerPtr; BEAST_EXPECT(offer[sfTakerGets] == XRP(2000)); @@ -878,7 +878,7 @@ public: auto const bobOffers = offersOnAccount(env, bob); BEAST_EXPECT(bobOffers.size() == 1); - for (auto offerPtr : bobOffers) + for (auto const& offerPtr : bobOffers) { auto const& offer = *offerPtr; BEAST_EXPECT(offer[sfTakerGets] == USD(1000)); @@ -927,7 +927,7 @@ public: auto const bobOffers = offersOnAccount(env, "bob"); BEAST_EXPECT(bobOffers.size() == 1); - for (auto offerPtr : bobOffers) + for (auto const& offerPtr : bobOffers) { auto const& offer = *offerPtr; BEAST_EXPECT(offer[sfTakerGets] == USD(499.5)); @@ -1208,7 +1208,7 @@ public: { auto acctOffers = offersOnAccount(env, account_to_test); - BEAST_EXPECT(acctOffers.size() == 0); + BEAST_EXPECT(acctOffers.empty()); for (auto const& offerPtr : acctOffers) { auto const& offer = *offerPtr; @@ -1926,9 +1926,13 @@ public: { Env env{*this, features}; if (NumberSwitchOver) + { env.enableFeature(fixUniversalNumber); + } else + { env.disableFeature(fixUniversalNumber); + } auto const gw = Account{"gateway"}; auto const alice = Account{"alice"}; @@ -2249,7 +2253,7 @@ public: // The gateway optionally creates an offer that would be crossed. auto const book = t.bookAmount; - if (book) + if (book != 0) env(offer(gw, XRP(book), USD(book))); env.close(); std::uint32_t const gwOfferSeq = env.seq(gw) - 1; @@ -2281,7 +2285,7 @@ public: auto acctOffers = offersOnAccount(env, acct); BEAST_EXPECT(acctOffers.size() == t.offers); - if (acctOffers.size() && t.offers) + if (!acctOffers.empty() && (t.offers != 0)) { auto const& acctOffer = *(acctOffers.front()); @@ -2292,7 +2296,7 @@ public: if (t.preTrust == noPreTrust) { - if (t.balanceUsd.value().signum()) + if (t.balanceUsd.value().signum() != 0) { // Verify the correct contents of the trustline verifyDefaultTrustline(env, acct, t.balanceUsd); @@ -2592,7 +2596,7 @@ public: // In pre-flow code alice's offer is left empty in the ledger. auto const aliceOffers = offersOnAccount(env, alice); - if (aliceOffers.size() != 0) + if (!aliceOffers.empty()) { BEAST_EXPECT(aliceOffers.size() == 1); auto const& aliceOffer = *(aliceOffers.front()); @@ -2769,10 +2773,10 @@ public: env.require(offers(acct, t.offers)); env.require(owners(acct, t.owners)); - if (t.offers) + if (t.offers != 0) { auto const acctOffers = offersOnAccount(env, acct); - if (acctOffers.size() > 0) + if (!acctOffers.empty()) { BEAST_EXPECT(acctOffers.size() == 1); auto const& acctOffer = *(acctOffers.front()); @@ -2984,7 +2988,7 @@ public: env.require(balance(eve, XRP(18000))); auto const evesOffers = offersOnAccount(env, eve); BEAST_EXPECT(evesOffers.size() == 1); - if (evesOffers.size() != 0) + if (!evesOffers.empty()) { auto const& evesOffer = *(evesOffers.front()); BEAST_EXPECT(evesOffer[sfLedgerEntryType] == ltOFFER); @@ -3160,7 +3164,7 @@ public: // In pre-flow code ova's offer is left empty in the ledger. auto const ovasOffers = offersOnAccount(env, ova); - if (ovasOffers.size() != 0) + if (!ovasOffers.empty()) { BEAST_EXPECT(ovasOffers.size() == 1); auto const& ovasOffer = *(ovasOffers.front()); @@ -3314,7 +3318,7 @@ public: env.close(); std::uint32_t const firstOfferSeq = env.seq(acct) - 1; - int offerCount = t.firstOfferTec == tesSUCCESS ? 1 : 0; + int offerCount = isTesSuccess(t.firstOfferTec) ? 1 : 0; env.require(owners(acct, 2 + offerCount)); env.require(balance(acct, t.fundUSD)); env.require(balance(acct, t.fundEUR)); @@ -3333,7 +3337,7 @@ public: env.close(); std::uint32_t const secondOfferSeq = env.seq(acct) - 1; - offerCount = t.secondOfferTec == tesSUCCESS ? 1 : offerCount; + offerCount = isTesSuccess(t.secondOfferTec) ? 1 : offerCount; env.require(owners(acct, 2 + offerCount)); env.require(balance(acct, t.fundUSD)); env.require(balance(acct, t.fundEUR)); @@ -3871,10 +3875,10 @@ public: // clang-format off TestData const tests[]{ // btcStart --------------------- actor[0] --------------------- -------------------- actor[1] ------------------- - {0, 0, 1, BTC(20), {{"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 - {0, 1, 0, BTC(20), {{"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 - {0, 0, 0, BTC(20), {{"cam", 0, drops(4000000'000000 - 5 * baseFee), BTC(20.0), USD(2000)} }}, // no xfer fee - {0, 1, 0, BTC( 5), {{"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 + {0, 0, 1, BTC(20), {{"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 + {0, 1, 0, BTC(20), {{"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 + {0, 0, 0, BTC(20), {{"cam", 0, drops(4000000'000000 - (5 * baseFee)), BTC(20.0), USD(2000)} }}, // no xfer fee + {0, 1, 0, BTC( 5), {{"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 }; // clang-format on @@ -4022,8 +4026,8 @@ public: // clang-format off TestData const tests[]{ // btcStart ------------------- actor[0] -------------------- ------------------- actor[1] -------------------- - {0, 0, 1, BTC(5), {{"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 - {0, 0, 0, BTC(5), {{"hye", 2, drops(4000000'000000 - 5 * baseFee), BTC(5), USD(2000)} }} // no xfer fee + {0, 0, 1, BTC(5), {{"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 + {0, 0, 0, BTC(5), {{"hye", 2, drops(4000000'000000 - (5 * baseFee)), BTC(5), USD(2000)} }} // no xfer fee }; // clang-format on @@ -4275,8 +4279,7 @@ public: Env env{*this, features}; - // This test mimics the payment flow used in the Ripple Connect - // smoke test. The players: + // This test mimics a payment flow. The players: // A USD gateway with hot and cold wallets // A EUR gateway with hot and cold walllets // A MM gateway that will provide offers from USD->EUR and EUR->USD @@ -4593,8 +4596,10 @@ public: std::map> offers; forEachItem(*env.current(), alice, [&](std::shared_ptr const& sle) { if (sle->getType() == ltOFFER) + { offers.emplace( (*sle)[sfSequence], std::make_pair((*sle)[sfTakerPays], (*sle)[sfTakerGets])); + } }); // first offer @@ -4749,7 +4754,7 @@ public: // Verify that the third offer alice created was consumed. { auto offers = sortedOffersOnAccount(env, alice); - BEAST_EXPECT(offers.size() == 0); + BEAST_EXPECT(offers.empty()); } env.require(balance(alice, USD(0))); env.require(owners(alice, 1)); @@ -4931,7 +4936,7 @@ public: makerXRPBalance -= txfee(env, 1); takerXRPBalance -= txfee(env, 1); - if (err == tesSUCCESS) + if (isTesSuccess(err)) { makerUSDBalance -= USD(100); takerUSDBalance += USD(100); @@ -4948,7 +4953,7 @@ public: makerXRPBalance -= txfee(env, 1); takerXRPBalance -= txfee(env, 1); - if (err == tesSUCCESS) + if (isTesSuccess(err)) { makerUSDBalance += USD(100); takerUSDBalance -= USD(100); @@ -4965,7 +4970,7 @@ public: makerXRPBalance -= txfee(env, 1); takerXRPBalance -= txfee(env, 1); - if (err == tesSUCCESS) + if (isTesSuccess(err)) { makerUSDBalance += USD(100); takerUSDBalance -= USD(100); diff --git a/src/test/app/Oracle_test.cpp b/src/test/app/Oracle_test.cpp index 9da0ee3a31..83b658ac41 100644 --- a/src/test/app/Oracle_test.cpp +++ b/src/test/app/Oracle_test.cpp @@ -23,7 +23,7 @@ private: Env env(*this); Account const bad("bad"); env.memoize(bad); - Oracle oracle( + Oracle const oracle( env, {.owner = bad, .seq = seq(1), @@ -35,7 +35,7 @@ private: { Env env(*this); env.fund(env.current()->fees().accountReserve(0), owner); - Oracle oracle( + Oracle const oracle( env, {.owner = owner, .fee = static_cast(env.current()->fees().base.drops()), @@ -302,7 +302,7 @@ private: Env env(*this); auto const baseFee = static_cast(env.current()->fees().base.drops()); env.fund(XRP(1'000), owner); - Oracle oracle( + Oracle const oracle( env, {.owner = owner, .series = {{"USD", "USD", 740, 1}}, @@ -315,7 +315,7 @@ private: Env env(*this); auto const baseFee = static_cast(env.current()->fees().base.drops()); env.fund(XRP(1'000), owner); - Oracle oracle( + Oracle const oracle( env, {.owner = owner, .series = {{"USD", "BTC", 740, maxPriceScale + 1}}, @@ -354,7 +354,7 @@ private: Env env(*this); env.fund(XRP(1'000), owner); Oracle oracle(env, {.owner = owner, .fee = -1, .err = ter(temBAD_FEE)}); - Oracle oracle1( + Oracle const oracle1( env, {.owner = owner, .fee = static_cast(env.current()->fees().base.drops())}); oracle.set(UpdateArg{.owner = owner, .fee = -1, .err = ter(temBAD_FEE)}); } @@ -371,7 +371,7 @@ private: auto const baseFee = static_cast(env.current()->fees().base.drops()); env.fund(XRP(1'000), owner); auto const count = ownerCount(env, owner); - Oracle oracle(env, {.owner = owner, .series = series, .fee = baseFee}); + Oracle const oracle(env, {.owner = owner, .series = series, .fee = baseFee}); BEAST_EXPECT(oracle.exists()); BEAST_EXPECT(ownerCount(env, owner) == (count + adj)); auto const entry = oracle.ledgerEntry(); @@ -506,9 +506,9 @@ private: auto const acctDelFee{drops(env.current()->fees().increment)}; env.fund(XRP(1'000), owner); env.fund(XRP(1'000), alice); - Oracle oracle( + Oracle const oracle( env, {.owner = owner, .series = {{"XRP", "USD", 740, 1}}, .fee = baseFee}); - Oracle oracle1( + Oracle const oracle1( env, {.owner = owner, .documentID = 2, @@ -764,7 +764,7 @@ private: env.fund(XRP(1'000), owner); { - Oracle oracle(env, {.owner = owner, .fee = baseFee, .err = ter(temDISABLED)}); + Oracle const oracle(env, {.owner = owner, .fee = baseFee, .err = ter(temDISABLED)}); } { diff --git a/src/test/app/OversizeMeta_test.cpp b/src/test/app/OversizeMeta_test.cpp index d5e2948f5b..d6305bedb6 100644 --- a/src/test/app/OversizeMeta_test.cpp +++ b/src/test/app/OversizeMeta_test.cpp @@ -9,7 +9,7 @@ namespace test { class PlumpBook_test : public beast::unit_test::suite { public: - void + static void createOffers(jtx::Env& env, jtx::IOU const& iou, std::size_t n) { using namespace jtx; @@ -64,7 +64,7 @@ BEAST_DEFINE_TESTSUITE(ThinBook, app, xrpl); class OversizeMeta_test : public beast::unit_test::suite { public: - void + static void createOffers(jtx::Env& env, jtx::IOU const& iou, std::size_t n) { using namespace jtx; @@ -123,12 +123,14 @@ public: len -= l2 + 1; } else + { len = l2; + } } return lo; } - void + static void createOffers(jtx::Env& env, jtx::IOU const& iou, std::size_t n) { using namespace jtx; diff --git a/src/test/app/Path_test.cpp b/src/test/app/Path_test.cpp index c2de4f3f08..841847d183 100644 --- a/src/test/app/Path_test.cpp +++ b/src/test/app/Path_test.cpp @@ -39,7 +39,7 @@ rpf(jtx::Account const& src, jtx::Account const& dst, std::uint32_t num_src) { auto& sc = (jv[jss::source_currencies] = Json::arrayValue); Json::Value j = Json::objectValue; - while (num_src--) + while ((num_src--) != 0u) { j[jss::currency] = std::to_string(num_src + 100); sc.append(j); @@ -100,7 +100,7 @@ public: void signal() { - std::lock_guard lk(mutex_); + std::lock_guard const lk(mutex_); signaled_ = true; cv_.notify_all(); } @@ -206,6 +206,8 @@ public: Json::Value p; p["Paths"] = path[jss::paths_computed]; STParsedJSONObject po("generic", p); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) paths = po.object->getFieldPathSet(sfPaths); } } @@ -957,13 +959,13 @@ public: (domainEnabled ? " w/ " : " w/o ") + "domain"); using namespace jtx; Env env = pathTestEnv(); - Account A1{"A1"}; - Account A2{"A2"}; - Account A3{"A3"}; - Account G1{"G1"}; - Account G2{"G2"}; - Account G3{"G3"}; - Account M1{"M1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const A3{"A3"}; + Account const G1{"G1"}; + Account const G2{"G2"}; + Account const G3{"G3"}; + Account const M1{"M1"}; env.fund(XRP(100000), A1); env.fund(XRP(10000), A2); @@ -1058,10 +1060,10 @@ public: "domain"); using namespace jtx; Env env = pathTestEnv(); - Account A1{"A1"}; - Account A2{"A2"}; - Account G3{"G3"}; - Account M1{"M1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const G3{"G3"}; + Account const M1{"M1"}; env.fund(XRP(1000), A1, A2, G3); env.fund(XRP(11000), M1); @@ -1118,11 +1120,11 @@ public: (domainEnabled ? " w/ " : " w/o ") + "domain"); using namespace jtx; Env env = pathTestEnv(); - Account A1{"A1"}; - Account A2{"A2"}; - Account G1BS{"G1BS"}; - Account G2SW{"G2SW"}; - Account M1{"M1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const G1BS{"G1BS"}; + Account const G2SW{"G2SW"}; + Account const M1{"M1"}; env.fund(XRP(1000), G1BS, G2SW, A1, A2); env.fund(XRP(11000), M1); @@ -1204,16 +1206,16 @@ public: (domainEnabled ? " w/ " : " w/o ") + "domain"); using namespace jtx; Env env = pathTestEnv(); - Account A1{"A1"}; - Account A2{"A2"}; - Account A3{"A3"}; - Account A4{"A4"}; - Account G1{"G1"}; - Account G2{"G2"}; - Account G3{"G3"}; - Account G4{"G4"}; - Account M1{"M1"}; - Account M2{"M2"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const A3{"A3"}; + Account const A4{"A4"}; + Account const G1{"G1"}; + Account const G2{"G2"}; + Account const G3{"G3"}; + Account const G4{"G4"}; + Account const M1{"M1"}; + Account const M2{"M2"}; env.fund(XRP(1000), A1, A2, A3, G1, G2, G3, G4); env.fund(XRP(10000), A4); @@ -1347,12 +1349,12 @@ public: (domainEnabled ? " w/ " : " w/o ") + "domain"); using namespace jtx; Env env = pathTestEnv(); - Account A1{"A1"}; - Account A2{"A2"}; - Account A3{"A3"}; - Account G1{"G1"}; - Account G2{"G2"}; - Account M1{"M1"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const A3{"A3"}; + Account const G1{"G1"}; + Account const G2{"G2"}; + Account const M1{"M1"}; env.fund(XRP(11000), M1); env.fund(XRP(1000), A1, A2, A3, G1, G2); @@ -1516,7 +1518,7 @@ public: } else { - BEAST_EXPECT(st.size() == 0); + BEAST_EXPECT(st.empty()); BEAST_EXPECT(equal(sa, XRP(0))); } }; @@ -1537,16 +1539,16 @@ public: // lambda param that creates different types of offers auto testPathfind = [&](auto func, bool const domainEnabled = false) { Env env = pathTestEnv(); - Account A1{"A1"}; - Account A2{"A2"}; - Account A3{"A3"}; - Account A4{"A4"}; - Account G1{"G1"}; - Account G2{"G2"}; - Account G3{"G3"}; - Account G4{"G4"}; - Account M1{"M1"}; - Account M2{"M2"}; + Account const A1{"A1"}; + Account const A2{"A2"}; + Account const A3{"A3"}; + Account const A4{"A4"}; + Account const G1{"G1"}; + Account const G2{"G2"}; + Account const G3{"G3"}; + Account const G4{"G4"}; + Account const M1{"M1"}; + Account const M2{"M2"}; env.fund(XRP(1000), A1, A2, A3, G1, G2, G3, G4); env.fund(XRP(10000), A4); @@ -1813,9 +1815,9 @@ public: testcase("AMM not used in domain path"); using namespace jtx; Env env = pathTestEnv(); - PermissionedDEX permDex(env); + PermissionedDEX const permDex(env); auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = permDex; - AMM amm(env, alice, XRP(10), USD(50)); + AMM const amm(env, alice, XRP(10), USD(50)); STPathSet st; STAmount sa, da; diff --git a/src/test/app/PayChan_test.cpp b/src/test/app/PayChan_test.cpp index b217bb3687..768031c3af 100644 --- a/src/test/app/PayChan_test.cpp +++ b/src/test/app/PayChan_test.cpp @@ -401,18 +401,23 @@ struct PayChan_test : public beast::unit_test::suite // Owner closes, will close after settleDelay env(claim(alice, chan), txflags(tfClose)); auto counts = [](auto const& t) { return t.time_since_epoch().count(); }; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*channelExpiration(*env.current(), chan) == counts(minExpiration)); // increase the expiration time env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration + 100s})); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*channelExpiration(*env.current(), chan) == counts(minExpiration) + 100); // decrease the expiration, but still above minExpiration env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration + 50s})); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*channelExpiration(*env.current(), chan) == counts(minExpiration) + 50); // decrease the expiration below minExpiration env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration - 50s}), ter(temBAD_EXPIRATION)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*channelExpiration(*env.current(), chan) == counts(minExpiration) + 50); env(claim(bob, chan), txflags(tfRenew), ter(tecNO_PERMISSION)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*channelExpiration(*env.current(), chan) == counts(minExpiration) + 50); env(claim(alice, chan), txflags(tfRenew)); BEAST_EXPECT(!channelExpiration(*env.current(), chan)); @@ -1010,9 +1015,11 @@ struct PayChan_test : public beast::unit_test::suite BEAST_EXPECT(r[jss::result][jss::validated]); BEAST_EXPECT(chan1Str != chan2Str); for (auto const& c : {chan1Str, chan2Str}) + { BEAST_EXPECT( r[jss::result][jss::channels][0u][jss::channel_id] == c || r[jss::result][jss::channels][1u][jss::channel_id] == c); + } } } @@ -1089,7 +1096,7 @@ struct PayChan_test : public beast::unit_test::suite { auto leftToFind = bobsB58; auto const numFull = bobs.size() / limit; - auto const numNonFull = bobs.size() % limit ? 1 : 0; + auto const numNonFull = ((bobs.size() % limit) != 0u) ? 1 : 0; Json::Value marker = Json::nullValue; @@ -1115,7 +1122,7 @@ struct PayChan_test : public beast::unit_test::suite testIt(expectMarker, limit); } - if (numNonFull) + if (numNonFull != 0) { testIt(false, bobs.size() % limit); } @@ -1244,9 +1251,11 @@ struct PayChan_test : public beast::unit_test::suite BEAST_EXPECT(r[jss::result][jss::validated]); BEAST_EXPECT(chan1Str != chan2Str); for (auto const& c : {chan1Str, chan2Str}) + { BEAST_EXPECT( r[jss::result][jss::channels][0u][jss::channel_id] == c || r[jss::result][jss::channels][1u][jss::channel_id] == c); + } } auto sliceToHex = [](Slice const& slice) { @@ -1495,7 +1504,7 @@ struct PayChan_test : public beast::unit_test::suite auto const settleDelay = 3600s; auto const channelFunds = XRP(1000); - std::optional cancelAfter; + std::optional const cancelAfter; { auto const chan = to_string(channel(alice, bob, env.seq(alice))); diff --git a/src/test/app/PayStrand_test.cpp b/src/test/app/PayStrand_test.cpp index 8c2ae698d0..222bc2ed07 100644 --- a/src/test/app/PayStrand_test.cpp +++ b/src/test/app/PayStrand_test.cpp @@ -176,15 +176,17 @@ class ElementComboIter bool has(SB s) const { - return state_ & (1 << safe_cast(s)); + return (state_ & (1 << safe_cast(s))) != 0; } bool hasAny(std::initializer_list sb) const { for (auto const s : sb) + { if (has(s)) return true; + } return false; } @@ -194,8 +196,10 @@ class ElementComboIter size_t result = 0; for (auto const s : sb) + { if (has(s)) result++; + } return result; } @@ -208,7 +212,7 @@ public: valid() const { return (allowCompound_ || !(has(SB::acc) && hasAny({SB::cur, SB::iss}))) && - (!hasAny({SB::prevAcc, SB::prevCur, SB::prevIss}) || prev_) && + (!hasAny({SB::prevAcc, SB::prevCur, SB::prevIss}) || (prev_ != nullptr)) && (!hasAny({SB::rootAcc, SB::sameAccIss, SB::existingAcc, SB::prevAcc}) || has(SB::acc)) && (!hasAny({SB::rootIss, SB::sameAccIss, SB::existingIss, SB::prevIss}) || @@ -275,13 +279,17 @@ public: return currencyF(); }(); if (!has(SB::boundary)) + { col.emplace_back(acc, cur, iss); + } else + { col.emplace_back( STPathElement::Type::typeBoundary, acc.value_or(AccountID{}), cur.value_or(Currency{}), iss.value_or(AccountID{})); + } } }; @@ -368,11 +376,17 @@ struct ExistingElementPool for (size_t id = 0; id < numCur; ++id) { if (id < 10) + { snprintf(buf, bufSize, "CC%zu", id); + } else if (id < 100) + { snprintf(buf, bufSize, "C%zu", id); + } else + { snprintf(buf, bufSize, "%zu", id); + } currencies.emplace_back(to_currency(buf)); currencyNames.emplace_back(buf); } @@ -407,8 +421,10 @@ struct ExistingElementPool std::vector ious; ious.reserve(numAct * numCur); for (auto const& a : accounts) + { for (auto const& cn : currencyNames) ious.emplace_back(a[cn]); + } // create offers from every currency to every other currency for (auto takerPays = ious.begin(), ie = ious.end(); takerPays != ie; ++takerPays) @@ -543,7 +559,7 @@ struct ExistingElementPool result.reserve(resultSize); while (outer.next()) { - StateGuard og{*this}; + StateGuard const og{*this}; outerResult = prefix; outer.emplace_into( outerResult, accF, issF, currencyF, existingAcc, existingCur, existingIss); @@ -551,7 +567,7 @@ struct ExistingElementPool ElementComboIter inner(prevInner); while (inner.next()) { - StateGuard ig{*this}; + StateGuard const ig{*this}; result = outerResult; inner.emplace_into( result, accF, issF, currencyF, existingAcc, existingCur, existingIss); @@ -607,7 +623,7 @@ struct PayStrand_test : public beast::unit_test::suite OfferCrossing::no, ammContext, std::nullopt, - env.app().logs().journal("Flow")); + env.app().getJournal("Flow")); BEAST_EXPECT(ter == expTer); if (sizeof...(expSteps) != 0) BEAST_EXPECT(equal(strand, std::forward(expSteps)...)); @@ -634,9 +650,9 @@ struct PayStrand_test : public beast::unit_test::suite OfferCrossing::no, ammContext, std::nullopt, - env.app().logs().journal("Flow")); + env.app().getJournal("Flow")); (void)_; - BEAST_EXPECT(ter == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(ter)); } { STPath const path = STPath({ipe(USD), cpe(xrpCurrency())}); @@ -652,9 +668,9 @@ struct PayStrand_test : public beast::unit_test::suite OfferCrossing::no, ammContext, std::nullopt, - env.app().logs().journal("Flow")); + env.app().getJournal("Flow")); (void)_; - BEAST_EXPECT(ter == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(ter)); } } @@ -749,7 +765,7 @@ struct PayStrand_test : public beast::unit_test::suite { // The root account can't be the src or dst - auto flowJournal = env.app().logs().journal("Flow"); + auto flowJournal = env.app().getJournal("Flow"); { // The root account can't be the dst auto r = toStrand( @@ -920,8 +936,8 @@ struct PayStrand_test : public beast::unit_test::suite OfferCrossing::no, ammContext, std::nullopt, - env.app().logs().journal("Flow")); - BEAST_EXPECT(ter == tesSUCCESS); + env.app().getJournal("Flow")); + BEAST_EXPECT(isTesSuccess(ter)); BEAST_EXPECT(equal(strand, D{alice, gw, usdC})); } @@ -948,8 +964,8 @@ struct PayStrand_test : public beast::unit_test::suite OfferCrossing::no, ammContext, std::nullopt, - env.app().logs().journal("Flow")); - BEAST_EXPECT(ter == tesSUCCESS); + env.app().getJournal("Flow")); + BEAST_EXPECT(isTesSuccess(ter)); BEAST_EXPECT(equal( strand, D{alice, gw, usdC}, B{USD.issue(), xrpIssue(), std::nullopt}, XRPS{bob})); } @@ -990,7 +1006,7 @@ struct PayStrand_test : public beast::unit_test::suite return result; }(); - PathSet paths(p); + PathSet const paths(p); env(pay(alice, alice, EUR(1)), json(paths.json()), @@ -1109,12 +1125,12 @@ struct PayStrand_test : public beast::unit_test::suite Env env(*this, features); env.fund(XRP(10000), alice, bob, gw); - STAmount sendMax{USD.issue(), 100, 1}; - STAmount noAccountAmount{Issue{USD.currency, noAccount()}, 100, 1}; - STAmount deliver; + STAmount const sendMax{USD.issue(), 100, 1}; + STAmount const noAccountAmount{Issue{USD.currency, noAccount()}, 100, 1}; + STAmount const deliver; AccountID const srcAcc = alice.id(); - AccountID dstAcc = bob.id(); - STPathSet pathSet; + AccountID const dstAcc = bob.id(); + STPathSet const pathSet; ::xrpl::path::RippleCalc::Input inputs; inputs.defaultPathsAllowed = true; try @@ -1129,7 +1145,7 @@ struct PayStrand_test : public beast::unit_test::suite noAccount(), pathSet, std::nullopt, - env.app().logs(), + env.app(), &inputs); BEAST_EXPECT(r.result() == temBAD_PATH); } @@ -1142,7 +1158,7 @@ struct PayStrand_test : public beast::unit_test::suite srcAcc, pathSet, std::nullopt, - env.app().logs(), + env.app(), &inputs); BEAST_EXPECT(r.result() == temBAD_PATH); } @@ -1155,7 +1171,7 @@ struct PayStrand_test : public beast::unit_test::suite srcAcc, pathSet, std::nullopt, - env.app().logs(), + env.app(), &inputs); BEAST_EXPECT(r.result() == temBAD_PATH); } @@ -1168,7 +1184,7 @@ struct PayStrand_test : public beast::unit_test::suite srcAcc, pathSet, std::nullopt, - env.app().logs(), + env.app(), &inputs); BEAST_EXPECT(r.result() == temBAD_PATH); } diff --git a/src/test/app/PermissionedDEX_test.cpp b/src/test/app/PermissionedDEX_test.cpp index 3cb732b9d1..58a041ff56 100644 --- a/src/test/app/PermissionedDEX_test.cpp +++ b/src/test/app/PermissionedDEX_test.cpp @@ -34,13 +34,13 @@ using namespace jtx; class PermissionedDEX_test : public beast::unit_test::suite { - [[nodiscard]] bool + [[nodiscard]] static bool offerExists(Env const& env, Account const& account, std::uint32_t offerSeq) { return static_cast(env.le(keylet::offer(account.id(), offerSeq))); } - [[nodiscard]] bool + [[nodiscard]] static bool checkOffer( Env const& env, Account const& account, @@ -120,13 +120,13 @@ class PermissionedDEX_test : public beast::unit_test::suite return true; } - uint256 + static uint256 getBookDirKey(Book const& book, STAmount const& takerPays, STAmount const& takerGets) { return keylet::quality(keylet::book(book), getRate(takerGets, takerPays)).key; } - std::optional + static std::optional getDefaultOfferDirKey(Env const& env, Account const& account, std::uint32_t offerSeq) { if (auto const sle = env.le(keylet::offer(account.id(), offerSeq))) @@ -135,7 +135,7 @@ class PermissionedDEX_test : public beast::unit_test::suite return {}; } - [[nodiscard]] bool + [[nodiscard]] static bool checkDirectorySize(Env const& env, uint256 directory, std::uint32_t dirSize) { std::optional pageIndex{0}; @@ -143,14 +143,15 @@ class PermissionedDEX_test : public beast::unit_test::suite do { - auto const page = env.le(keylet::page(directory, *pageIndex)); + auto const page = env.le( + keylet::page(directory, *pageIndex)); // NOLINT(bugprone-unchecked-optional-access) if (!page) break; pageIndex = (*page)[~sfIndexNext]; dirCnt += (*page)[sfIndexes].size(); - } while (pageIndex.value_or(0)); + } while (pageIndex.value_or(0) != 0u); return dirCnt == dirSize; } @@ -182,7 +183,7 @@ class PermissionedDEX_test : public beast::unit_test::suite PermissionedDEX(env); // create devin account who is not part of the domain - Account devin("devin"); + Account const devin("devin"); env.fund(XRP(1000), devin); env.close(); env.trust(USD(1000), devin); @@ -215,7 +216,7 @@ class PermissionedDEX_test : public beast::unit_test::suite PermissionedDEX(env); // create devin account who is not part of the domain - Account devin("devin"); + Account const devin("devin"); env.fund(XRP(1000), devin); env.close(); env.trust(USD(1000), devin); @@ -401,7 +402,7 @@ class PermissionedDEX_test : public beast::unit_test::suite env.close(); // create devin account who is not part of the domain - Account devin("devin"); + Account const devin("devin"); env.fund(XRP(1000), devin); env.close(); env.trust(USD(1000), devin); @@ -447,7 +448,7 @@ class PermissionedDEX_test : public beast::unit_test::suite env.close(); // create devin account who is not part of the domain - Account devin("devin"); + Account const devin("devin"); env.fund(XRP(1000), devin); env.close(); env.trust(USD(1000), devin); @@ -524,7 +525,8 @@ class PermissionedDEX_test : public beast::unit_test::suite auto const regularDirKey = getDefaultOfferDirKey(env, bob, regularOfferSeq); BEAST_EXPECT(regularDirKey); - BEAST_EXPECT(checkDirectorySize(env, *regularDirKey, 1)); + BEAST_EXPECT(checkDirectorySize( + env, *regularDirKey, 1)); // NOLINT(bugprone-unchecked-optional-access) // a domain payment cannot consume regular offers env(pay(alice, carol, USD(10)), @@ -543,7 +545,8 @@ class PermissionedDEX_test : public beast::unit_test::suite auto const domainDirKey = getDefaultOfferDirKey(env, bob, domainOfferSeq); BEAST_EXPECT(domainDirKey); - BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 1)); + BEAST_EXPECT(checkDirectorySize( + env, *domainDirKey, 1)); // NOLINT(bugprone-unchecked-optional-access) // cross-currency permissioned payment consumed // domain offer instead of regular offer @@ -553,8 +556,10 @@ class PermissionedDEX_test : public beast::unit_test::suite BEAST_EXPECT(checkOffer(env, bob, regularOfferSeq, XRP(10), USD(10))); // domain directory is empty - BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 0)); - BEAST_EXPECT(checkDirectorySize(env, *regularDirKey, 1)); + BEAST_EXPECT(checkDirectorySize( + env, *domainDirKey, 0)); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT(checkDirectorySize( + env, *regularDirKey, 1)); // NOLINT(bugprone-unchecked-optional-access) } // test domain payment consuming two offers in the path @@ -637,7 +642,7 @@ class PermissionedDEX_test : public beast::unit_test::suite // Fund devin and create USD trustline Account badDomainOwner("badDomainOwner"); - Account devin("devin"); + Account const devin("devin"); env.fund(XRP(1000), badDomainOwner, devin); env.close(); env.trust(USD(1000), devin); @@ -646,7 +651,7 @@ class PermissionedDEX_test : public beast::unit_test::suite env.close(); auto const badCredType = "badCred"; - pdomain::Credentials credentials{{badDomainOwner, badCredType}}; + pdomain::Credentials const credentials{{badDomainOwner, badCredType}}; env(pdomain::setTx(badDomainOwner, credentials)); auto objects = pdomain::getObjects(badDomainOwner, env); @@ -693,7 +698,7 @@ class PermissionedDEX_test : public beast::unit_test::suite env.close(); // fund devin but don't create a USD trustline with gateway - Account devin("devin"); + Account const devin("devin"); env.fund(XRP(1000), devin); env.close(); @@ -716,7 +721,7 @@ class PermissionedDEX_test : public beast::unit_test::suite PermissionedDEX(env); // create devin account who is not part of the domain - Account devin("devin"); + Account const devin("devin"); env.fund(XRP(1000), devin); env.close(); env.trust(USD(1000), devin); @@ -889,7 +894,8 @@ class PermissionedDEX_test : public beast::unit_test::suite auto const domainDirKey = getDefaultOfferDirKey(env, bob, bobOfferSeq); BEAST_EXPECT(domainDirKey); - BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 2)); + 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)); @@ -902,7 +908,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // alice's unfunded offer is removed implicitly BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq)); - BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 1)); + BEAST_EXPECT(checkDirectorySize( + env, *domainDirKey, 1)); // NOLINT(bugprone-unchecked-optional-access) } void @@ -913,7 +920,7 @@ class PermissionedDEX_test : public beast::unit_test::suite Env env(*this, features); auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - AMM 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)), @@ -1157,12 +1164,12 @@ class PermissionedDEX_test : public beast::unit_test::suite // Fund accounts Account badDomainOwner("badDomainOwner"); - Account devin("devin"); + Account const devin("devin"); env.fund(XRP(1000), badDomainOwner, devin); env.close(); auto const badCredType = "badCred"; - pdomain::Credentials credentials{{badDomainOwner, badCredType}}; + pdomain::Credentials const credentials{{badDomainOwner, badCredType}}; env(pdomain::setTx(badDomainOwner, credentials)); auto objects = pdomain::getObjects(badDomainOwner, env); @@ -1290,8 +1297,8 @@ class PermissionedDEX_test : public beast::unit_test::suite std::vector offerSeqs; offerSeqs.reserve(100); - Book domainBook{Issue(XRP), Issue(USD), domainID}; - Book openBook{Issue(XRP), Issue(USD), std::nullopt}; + Book const domainBook{Issue(XRP), Issue(USD), domainID}; + Book const openBook{Issue(XRP), Issue(USD), std::nullopt}; auto const domainDir = getBookDirKey(domainBook, XRP(10), USD(10)); auto const openDir = getBookDirKey(openBook, XRP(10), USD(10)); diff --git a/src/test/app/PermissionedDomains_test.cpp b/src/test/app/PermissionedDomains_test.cpp index d2a14ce82a..c4bde9831a 100644 --- a/src/test/app/PermissionedDomains_test.cpp +++ b/src/test/app/PermissionedDomains_test.cpp @@ -49,7 +49,7 @@ class PermissionedDomains_test : public beast::unit_test::suite Account const alice("alice"); Env env(*this, features); env.fund(XRP(1000), alice); - pdomain::Credentials credentials{{alice, "first credential"}}; + pdomain::Credentials const credentials{{alice, "first credential"}}; env(pdomain::setTx(alice, credentials)); BEAST_EXPECT(env.ownerCount(alice) == 1); auto objects = pdomain::getObjects(alice, env); @@ -71,7 +71,7 @@ class PermissionedDomains_test : public beast::unit_test::suite Account const alice("alice"); Env env(*this, amendments); env.fund(XRP(1000), alice); - pdomain::Credentials credentials{{alice, "first credential"}}; + pdomain::Credentials const credentials{{alice, "first credential"}}; env(pdomain::setTx(alice, credentials), ter(temDISABLED)); } @@ -83,7 +83,7 @@ class PermissionedDomains_test : public beast::unit_test::suite Account const alice("alice"); Env env(*this, withoutFeature_); env.fund(XRP(1000), alice); - pdomain::Credentials credentials{{alice, "first credential"}}; + pdomain::Credentials const credentials{{alice, "first credential"}}; env(pdomain::setTx(alice, credentials), ter(temDISABLED)); env(pdomain::deleteTx(alice, uint256(75)), ter(temDISABLED)); } @@ -199,9 +199,13 @@ class PermissionedDomains_test : public beast::unit_test::suite uint256 d; if (domain) + { d = *domain; + } else + { d = pdomain::getNewDomain(env.meta()); + } env.close(); auto objects = pdomain::getObjects(account, env); auto const fromObject = pdomain::credentialsFromJson(objects[d], human2Acc); @@ -228,9 +232,13 @@ class PermissionedDomains_test : public beast::unit_test::suite uint256 d; if (domain) + { d = *domain; + } else + { d = pdomain::getNewDomain(env.meta()); + } env.close(); auto objects = pdomain::getObjects(account, env); auto const fromObject = pdomain::credentialsFromJson(objects[d], human2Acc); @@ -383,7 +391,7 @@ class PermissionedDomains_test : public beast::unit_test::suite constexpr std::size_t deleteDelta = 255; { // Close enough ledgers to make it potentially deletable if empty. - std::size_t ownerSeq = env.seq(alice[0]); + std::size_t const ownerSeq = env.seq(alice[0]); while (deleteDelta + ownerSeq > env.current()->seq()) env.close(); env(acctdelete(alice[0], alice[2]), fee(acctDelFee), ter(tecHAS_OBLIGATIONS)); @@ -394,7 +402,7 @@ class PermissionedDomains_test : public beast::unit_test::suite for (auto const& objs : pdomain::getObjects(alice[0], env)) env(pdomain::deleteTx(alice[0], objs.first)); env.close(); - std::size_t ownerSeq = env.seq(alice[0]); + std::size_t const ownerSeq = env.seq(alice[0]); while (deleteDelta + ownerSeq > env.current()->seq()) env.close(); env(acctdelete(alice[0], alice[2]), fee(acctDelFee)); @@ -412,7 +420,7 @@ class PermissionedDomains_test : public beast::unit_test::suite env.fund(XRP(1000), alice); auto const setFee(drops(env.current()->fees().increment)); - pdomain::Credentials credentials{{alice, "first credential"}}; + pdomain::Credentials const credentials{{alice, "first credential"}}; env(pdomain::setTx(alice, credentials)); env.close(); @@ -476,10 +484,10 @@ 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 credentials{{alice, "first credential"}}; + pdomain::Credentials const credentials{{alice, "first credential"}}; env(pdomain::setTx(alice, credentials), ter(tecINSUFFICIENT_RESERVE)); BEAST_EXPECT(env.ownerCount(alice) == 0); - BEAST_EXPECT(pdomain::getObjects(alice, env).size() == 0); + BEAST_EXPECT(pdomain::getObjects(alice, env).empty()); env.close(); auto const baseFee = env.current()->fees().base.drops(); diff --git a/src/test/app/PseudoTx_test.cpp b/src/test/app/PseudoTx_test.cpp index aa16df8e9c..a9180d0e03 100644 --- a/src/test/app/PseudoTx_test.cpp +++ b/src/test/app/PseudoTx_test.cpp @@ -11,7 +11,7 @@ namespace test { struct PseudoTx_test : public beast::unit_test::suite { - std::vector + static std::vector getPseudoTxs(Rules const& rules, std::uint32_t seq) { std::vector res; @@ -43,7 +43,7 @@ struct PseudoTx_test : public beast::unit_test::suite return res; } - std::vector + static std::vector getRealTxs() { std::vector res; @@ -70,7 +70,7 @@ struct PseudoTx_test : public beast::unit_test::suite BEAST_EXPECT(isPseudoTx(stx)); BEAST_EXPECT(!passesLocalChecks(stx, reason)); BEAST_EXPECT(reason == "Cannot submit pseudo transactions."); - env.app().openLedger().modify([&](OpenView& view, beast::Journal j) { + env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) { auto const result = xrpl::apply(env.app(), view, stx, tapNONE, j); BEAST_EXPECT(!result.applied && result.ter == temINVALID); return result.applied; diff --git a/src/test/app/RCLValidations_test.cpp b/src/test/app/RCLValidations_test.cpp index 140ba9f1d7..32267cbf45 100644 --- a/src/test/app/RCLValidations_test.cpp +++ b/src/test/app/RCLValidations_test.cpp @@ -1,10 +1,10 @@ #include #include -#include #include #include +#include namespace xrpl { namespace test { @@ -55,13 +55,17 @@ class RCLValidations_test : public beast::unit_test::suite std::vector> history; jtx::Env env(*this); - Config config; + Config const config; auto prev = std::make_shared( - create_genesis, config, std::vector{}, env.app().getNodeFamily()); + create_genesis, + Rules{config.features}, + config.FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); history.push_back(prev); - for (auto i = 0; i < (2 * maxAncestors + 1); ++i) + for (auto i = 0; i < ((2 * maxAncestors) + 1); ++i) { - auto next = std::make_shared(*prev, env.app().timeKeeper().closeTime()); + auto next = std::make_shared(*prev, env.app().getTimeKeeper().closeTime()); next->updateSkipList(); history.push_back(next); prev = next; @@ -78,7 +82,7 @@ class RCLValidations_test : public beast::unit_test::suite bool forceHash = true; while (altHistory.size() < history.size()) { - auto next = std::make_shared(*prev, env.app().timeKeeper().closeTime()); + auto next = std::make_shared(*prev, env.app().getTimeKeeper().closeTime()); // Force a different hash on the first iteration next->updateSkipList(); BEAST_EXPECT(next->read(keylet::fees())); @@ -96,7 +100,7 @@ class RCLValidations_test : public beast::unit_test::suite // Empty ledger { - RCLValidatedLedger a{RCLValidatedLedger::MakeGenesis{}}; + RCLValidatedLedger const a{RCLValidatedLedger::MakeGenesis{}}; BEAST_EXPECT(a.seq() == Seq{0}); BEAST_EXPECT(a[Seq{0}] == ID{0}); BEAST_EXPECT(a.minSeq() == Seq{0}); @@ -104,17 +108,21 @@ class RCLValidations_test : public beast::unit_test::suite // Full history ledgers { - std::shared_ptr ledger = history.back(); - RCLValidatedLedger a{ledger, env.journal}; + std::shared_ptr const ledger = history.back(); + RCLValidatedLedger const a{ledger, env.journal}; BEAST_EXPECT(a.seq() == ledger->header().seq); BEAST_EXPECT(a.minSeq() == a.seq() - maxAncestors); // Ensure the ancestral 256 ledgers have proper ID for (Seq s = a.seq(); s > 0; s--) { if (s >= a.minSeq()) + { BEAST_EXPECT(a[s] == history[s - 1]->header().hash); + } else + { BEAST_EXPECT(a[s] == ID{0}); + } } } @@ -122,21 +130,21 @@ class RCLValidations_test : public beast::unit_test::suite // Empty with non-empty { - RCLValidatedLedger a{RCLValidatedLedger::MakeGenesis{}}; + RCLValidatedLedger const a{RCLValidatedLedger::MakeGenesis{}}; - for (auto ledger : {history.back(), history[maxAncestors - 1]}) + for (auto const& ledger : {history.back(), history[maxAncestors - 1]}) { - RCLValidatedLedger b{ledger, env.journal}; + RCLValidatedLedger const b{ledger, env.journal}; BEAST_EXPECT(mismatch(a, b) == 1); BEAST_EXPECT(mismatch(b, a) == 1); } } // Same chains, different seqs { - RCLValidatedLedger a{history.back(), env.journal}; + RCLValidatedLedger const a{history.back(), env.journal}; for (Seq s = a.seq(); s > 0; s--) { - RCLValidatedLedger b{history[s - 1], env.journal}; + RCLValidatedLedger const b{history[s - 1], env.journal}; if (s >= a.minSeq()) { BEAST_EXPECT(mismatch(a, b) == b.seq() + 1); @@ -154,8 +162,8 @@ class RCLValidations_test : public beast::unit_test::suite // Alt history diverged at history.size()/2 for (Seq s = 1; s < history.size(); ++s) { - RCLValidatedLedger a{history[s - 1], env.journal}; - RCLValidatedLedger b{altHistory[s - 1], env.journal}; + RCLValidatedLedger const a{history[s - 1], env.journal}; + RCLValidatedLedger const b{altHistory[s - 1], env.journal}; BEAST_EXPECT(a.seq() == b.seq()); if (s <= diverge) @@ -175,10 +183,10 @@ class RCLValidations_test : public beast::unit_test::suite // Different chains, different seqs { // Compare around the divergence point - RCLValidatedLedger a{history[diverge], env.journal}; + RCLValidatedLedger const a{history[diverge], env.journal}; for (Seq offset = diverge / 2; offset < 3 * diverge / 2; ++offset) { - RCLValidatedLedger b{altHistory[offset - 1], env.journal}; + RCLValidatedLedger const b{altHistory[offset - 1], env.journal}; if (offset <= diverge) { BEAST_EXPECT(mismatch(a, b) == b.seq() + 1); @@ -213,13 +221,17 @@ class RCLValidations_test : public beast::unit_test::suite // Generate a chain of 256 + 10 ledgers jtx::Env env(*this); auto& j = env.journal; - Config config; + Config const config; auto prev = std::make_shared( - create_genesis, config, std::vector{}, env.app().getNodeFamily()); + create_genesis, + Rules{config.features}, + config.FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); history.push_back(prev); for (auto i = 0; i < (maxAncestors + 10); ++i) { - auto next = std::make_shared(*prev, env.app().timeKeeper().closeTime()); + auto next = std::make_shared(*prev, env.app().getTimeKeeper().closeTime()); next->updateSkipList(); history.push_back(next); prev = next; diff --git a/src/test/app/ReducedOffer_test.cpp b/src/test/app/ReducedOffer_test.cpp index 737d73e462..b19999fecd 100644 --- a/src/test/app/ReducedOffer_test.cpp +++ b/src/test/app/ReducedOffer_test.cpp @@ -92,9 +92,11 @@ public: // alice's offer should be fully crossed and so gone from // the ledger. if (!BEAST_EXPECT(!offerInLedger(env, alice, aliceOfferSeq))) + { // If the in-ledger offer was not consumed then further // results are meaningless. return 1; + } // bob's offer should be in the ledger, but reduced in size. unsigned int badRate = 1; @@ -158,12 +160,12 @@ public: mantissaReduce <= 5'000'000'000ull; mantissaReduce += 20'000'000ull) { - STAmount aliceUSD{ + STAmount const aliceUSD{ bobOffer.out.issue(), bobOffer.out.mantissa() - mantissaReduce, bobOffer.out.exponent()}; - STAmount aliceXRP{bobOffer.in.issue(), bobOffer.in.mantissa() - 1}; - Amounts aliceOffer{aliceUSD, aliceXRP}; + STAmount const aliceXRP{bobOffer.in.issue(), bobOffer.in.mantissa() - 1}; + Amounts const aliceOffer{aliceUSD, aliceXRP}; blockedCount += exerciseOfferPair(aliceOffer, bobOffer); } @@ -290,12 +292,12 @@ public: mantissaReduce <= 4'000'000'000ull; mantissaReduce += 20'000'000ull) { - STAmount bobUSD{ + STAmount const bobUSD{ aliceOffer.out.issue(), aliceOffer.out.mantissa() - mantissaReduce, aliceOffer.out.exponent()}; - STAmount bobXRP{aliceOffer.in.issue(), aliceOffer.in.mantissa() - 1}; - Amounts bobOffer{bobUSD, bobXRP}; + STAmount const bobXRP{aliceOffer.in.issue(), aliceOffer.in.mantissa() - 1}; + Amounts const bobOffer{bobUSD, bobXRP}; blockedCount += exerciseOfferPair(aliceOffer, bobOffer); } @@ -443,7 +445,7 @@ public: // Examine the aftermath of alice's offer. { bool const bobOfferGone = !offerInLedger(env, bob, bobOfferSeq); - STAmount aliceBalanceUSD = env.balance(alice, USD); + STAmount const aliceBalanceUSD = env.balance(alice, USD); #if 0 std::cout << "bob initial: " << initialBobUSD @@ -489,7 +491,7 @@ public: } } - Amounts + static Amounts jsonOfferToAmounts(Json::Value const& json) { STAmount const in = amountFromJson(sfTakerPays, json[sfTakerPays.jsonName]); @@ -578,7 +580,7 @@ public: { Json::Value aliceOffer = ledgerEntryOffer(env, alice, aliceOfferSeq); - Amounts aliceReducedOffer = jsonOfferToAmounts(aliceOffer[jss::node]); + Amounts const aliceReducedOffer = jsonOfferToAmounts(aliceOffer[jss::node]); BEAST_EXPECT(aliceReducedOffer.in < aliceInitialOffer.in); BEAST_EXPECT(aliceReducedOffer.out < aliceInitialOffer.out); diff --git a/src/test/app/Regression_test.cpp b/src/test/app/Regression_test.cpp index e323e61132..59ab0e427d 100644 --- a/src/test/app/Regression_test.cpp +++ b/src/test/app/Regression_test.cpp @@ -45,21 +45,25 @@ struct Regression_test : public beast::unit_test::suite // be reproduced against an open ledger. Make a local // closed ledger and work with it directly. auto closed = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); auto expectedDrops = INITIAL_XRP; BEAST_EXPECT(closed->header().drops == expectedDrops); auto const aliceXRP = 400; auto const aliceAmount = XRP(aliceXRP); - auto next = std::make_shared(*closed, env.app().timeKeeper().closeTime()); + auto next = std::make_shared(*closed, env.app().getTimeKeeper().closeTime()); { // Fund alice auto const jt = env.jt(pay(env.master, "alice", aliceAmount)); OpenView accum(&*next); auto const result = xrpl::apply(env.app(), accum, *jt.stx, tapNONE, env.journal); - BEAST_EXPECT(result.ter == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(result.ter)); BEAST_EXPECT(result.applied); accum.apply(*next); @@ -109,7 +113,8 @@ struct Regression_test : public beast::unit_test::suite auto test256r1key = [&env](Account const& acct) { auto const baseFee = env.current()->fees().base; std::uint32_t const acctSeq = env.seq(acct); - Json::Value jsonNoop = env.json(noop(acct), fee(baseFee), seq(acctSeq), sig(acct)); + Json::Value const jsonNoop = + env.json(noop(acct), fee(baseFee), seq(acctSeq), sig(acct)); JTx jt = env.jt(jsonNoop); jt.fill_sig = false; @@ -233,8 +238,8 @@ struct Regression_test : public beast::unit_test::suite using namespace jtx; Env env(*this); - Account alice("alice"); - Account bob("bob"); + Account const alice("alice"); + Account const bob("bob"); env.fund(XRP(10'000), alice, bob); env.close(); @@ -269,8 +274,8 @@ struct Regression_test : public beast::unit_test::suite if (BEAST_EXPECT(bob_index.isNonZero()) && BEAST_EXPECT(digest.has_value())) { - auto& cache = env.app().cachedSLEs(); - cache.del(*digest, false); + auto& cache = env.app().getCachedSLEs(); + cache.del(*digest, false); // NOLINT(bugprone-unchecked-optional-access) auto const beforeCounts = mapCounts(CountedObjects::getInstance().getCounts(0)); env(check::cash(alice, bob_index, check::DeliverMin(XRP(100))), ter(tecNO_ENTRY)); diff --git a/src/test/app/SHAMapStore_test.cpp b/src/test/app/SHAMapStore_test.cpp index e247a7fa87..5dc6867f43 100644 --- a/src/test/app/SHAMapStore_test.cpp +++ b/src/test/app/SHAMapStore_test.cpp @@ -35,7 +35,7 @@ class SHAMapStore_test : public beast::unit_test::suite return cfg; } - bool + static bool goodLedger(jtx::Env& env, Json::Value const& json, std::string ledgerID, bool checkDB = false) { auto good = json.isMember(jss::result) && !RPC::contains_error(json[jss::result]) && @@ -74,7 +74,7 @@ class SHAMapStore_test : public beast::unit_test::suite outTxHash == ledger[jss::transaction_hash].asString(); } - bool + static bool bad(Json::Value const& json, error_code_i error = rpcLGR_NOT_FOUND) { return json.isMember(jss::result) && RPC::contains_error(json[jss::result]) && @@ -191,7 +191,7 @@ public: ledgers.emplace(std::make_pair(i, env.rpc("ledger", std::to_string(i)))); BEAST_EXPECT( goodLedger(env, ledgers[i], std::to_string(i), true) && - getHash(ledgers[i]).length()); + !getHash(ledgers[i]).empty()); } ledgerCheck(env, deleteInterval + 1, 2); @@ -233,7 +233,7 @@ public: store.getLastRotated() == lastRotated || i == lastRotated + deleteInterval - 2); BEAST_EXPECT( goodLedger(env, ledgers[i], std::to_string(i), true) && - getHash(ledgers[i]).length()); + !getHash(ledgers[i]).empty()); } store.rendezvous(); @@ -350,9 +350,9 @@ public: BEAST_EXPECT(lastRotated == store.getLastRotated()); // This does not kick off a cleanup - canDelete = env.rpc("can_delete", std::to_string(ledgerSeq + deleteInterval / 2)); + canDelete = env.rpc("can_delete", std::to_string(ledgerSeq + (deleteInterval / 2))); BEAST_EXPECT(!RPC::contains_error(canDelete[jss::result])); - BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == ledgerSeq + deleteInterval / 2); + BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == ledgerSeq + (deleteInterval / 2)); store.rendezvous(); @@ -485,7 +485,7 @@ public: section, megabytes(env.app().config().getValueFor(SizedItem::burstSize, std::nullopt)), scheduler, - env.app().logs().journal("NodeStoreTest"))}; + env.app().getJournal("NodeStoreTest"))}; backend->open(); return backend; } @@ -518,7 +518,7 @@ public: std::move(writableBackend), std::move(archiveBackend), nscfg, - env.app().logs().journal("NodeStoreTest")); + env.app().getJournal("NodeStoreTest")); ///////////////////////////////////////////////////////////// // Check basic functionality diff --git a/src/test/app/TheoreticalQuality_test.cpp b/src/test/app/TheoreticalQuality_test.cpp index 024ee0742e..d7e0882c3b 100644 --- a/src/test/app/TheoreticalQuality_test.cpp +++ b/src/test/app/TheoreticalQuality_test.cpp @@ -25,7 +25,9 @@ struct RippleCalcTestParams STPathSet paths; explicit RippleCalcTestParams(Json::Value const& jv) + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) : srcAccount{*parseBase58(jv[jss::Account].asString())} + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) , dstAccount{*parseBase58(jv[jss::Destination].asString())} , dstAmt{amountFromJson(sfAmount, jv[jss::Amount])} { @@ -45,6 +47,7 @@ struct RippleCalcTestParams { assert(!pe.isMember(jss::currency) && !pe.isMember(jss::issuer)); p.emplace_back( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) *parseBase58(pe[jss::account].asString()), std::nullopt, std::nullopt); @@ -54,9 +57,15 @@ struct RippleCalcTestParams auto const currency = to_currency(pe[jss::currency].asString()); std::optional issuer; if (!isXRP(currency)) + { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) issuer = *parseBase58(pe[jss::issuer].asString()); + } else + { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) assert(isXRP(*parseBase58(pe[jss::issuer].asString()))); + } p.emplace_back(std::nullopt, currency, issuer); } else @@ -144,7 +153,7 @@ public: jtx::Env& env, jtx::Account const& acc, jtx::Account const& peer, - Currency const& currency) + Currency const& currency) const { using namespace jtx; IOU const iou{acc, currency}; @@ -190,7 +199,7 @@ class TheoreticalQuality_test : public beast::unit_test::suite prettyQuality(Quality const& q) { std::stringstream sstr; - STAmount rate = q.rate(); + STAmount const rate = q.rate(); sstr << rate << " (" << q << ")"; return sstr.str(); }; @@ -211,7 +220,7 @@ class TheoreticalQuality_test : public beast::unit_test::suite std::shared_ptr closed, std::optional const& expectedQ = {}) { - PaymentSandbox sb(closed.get(), tapNONE); + PaymentSandbox const sb(closed.get(), tapNONE); AMMContext ammContext(rcp.srcAccount, false); auto const sendMaxIssue = [&rcp]() -> std::optional { @@ -220,7 +229,7 @@ class TheoreticalQuality_test : public beast::unit_test::suite return std::nullopt; }(); - beast::Journal dummyJ{beast::Journal::getNullSink()}; + beast::Journal const dummyJ{beast::Journal::getNullSink()}; auto sr = toStrands( sb, @@ -237,9 +246,9 @@ class TheoreticalQuality_test : public beast::unit_test::suite std::nullopt, dummyJ); - BEAST_EXPECT(sr.first == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(sr.first)); - if (sr.first != tesSUCCESS) + if (!isTesSuccess(sr.first)) return; // Due to the floating point calculations, theoretical and actual @@ -255,7 +264,8 @@ class TheoreticalQuality_test : public beast::unit_test::suite for (auto const& strand : sr.second) { - Quality const theoreticalQ = *qualityUpperBound(sb, strand); + Quality const theoreticalQ = + *qualityUpperBound(sb, strand); // NOLINT(bugprone-unchecked-optional-access) auto const f = flow(sb, strand, IOUAmount(10, 0), IOUAmount(5, 0), dummyJ); BEAST_EXPECT(f.success); @@ -285,8 +295,6 @@ public: { testcase("Direct Step"); - // clang-format off - // Set up a payment through four accounts: alice -> bob -> carol -> dan // For each relevant trust line on the path, there are three things that can vary: // 1) input quality @@ -295,8 +303,6 @@ public: // For each account, there is one thing that can vary: // 1) transfer rate - // clang-format on - using namespace jtx; auto const currency = to_currency("USD"); @@ -360,7 +366,7 @@ public: // Accounts are set up, make the payment IOU const iou{accounts.back(), currency}; - RippleCalcTestParams rcp{env.json( + RippleCalcTestParams const rcp{env.json( pay(accounts.front(), accounts.back(), iou(paymentAmount)), accountsPath, txflags(tfNoRippleDirect))}; @@ -375,14 +381,12 @@ public: testcase("Book Step"); using namespace jtx; - // clang-format off + // Setup a payment through an offer: + // alice (USD/bob) -> bob -> (USD/bob)|(EUR/carol) -> carol -> dan + // For each relevant trust line, vary input quality, output quality, debt direction. For + // each account, vary transfer rate. - // Setup a payment through an offer: alice (USD/bob) -> bob -> (USD/bob)|(EUR/carol) -> carol -> dan - // For each relevant trust line, vary input quality, output quality, debt direction. - // For each account, vary transfer rate. - // The USD/bob|EUR/carol offer owner is "Oscar" - - // clang-format on + // The USD/bob|EUR/carol offer owner is "Oscar". int const numTestIterations = reqNumIterations.value_or(100); @@ -409,7 +413,7 @@ public: auto const USDB = bob["USD"]; auto const EURC = carol["EUR"]; constexpr std::size_t const numAccounts = 5; - std::array accounts{{alice, bob, carol, dan, oscar}}; + std::array const accounts{{alice, bob, carol, dan, oscar}}; // sendmax should be in USDB and delivered amount should be in EURC // normalized path should be: @@ -441,7 +445,7 @@ public: // Accounts are set up, make the payment IOU const srcIOU{bob, usdCurrency}; IOU const dstIOU{carol, eurCurrency}; - RippleCalcTestParams rcp{env.json( + RippleCalcTestParams const rcp{env.json( pay(alice, dan, dstIOU(paymentAmount)), sendmax(srcIOU(100 * paymentAmount)), bookPath, @@ -488,7 +492,7 @@ public: return std::nullopt; try { - std::size_t pos; + std::size_t pos = 0; auto const r = stoi(s, &pos); if (pos != s.size()) return std::nullopt; diff --git a/src/test/app/Ticket_test.cpp b/src/test/app/Ticket_test.cpp index 760cce88be..7f96caa05f 100644 --- a/src/test/app/Ticket_test.cpp +++ b/src/test/app/Ticket_test.cpp @@ -9,7 +9,7 @@ namespace xrpl { class Ticket_test : public beast::unit_test::suite { - /// @brief Validate metadata for a successful CreateTicket transaction. + /// @brief Validate metadata for a successful TicketCreate transaction. /// /// @param env current jtx env (tx and meta are extracted using it) void @@ -286,11 +286,15 @@ class Ticket_test : public beast::unit_test::suite BEAST_EXPECT(prevTicketCount > 0); if (prevTicketCount == 1) + { BEAST_EXPECT(!finalFields.isMember(sfTicketCount.jsonName)); + } else + { BEAST_EXPECT( finalFields.isMember(sfTicketCount.jsonName) && finalFields[sfTicketCount.jsonName].asUInt() == prevTicketCount - 1); + } } } else if (node.isMember(sfDeletedNode.jsonName)) @@ -374,7 +378,7 @@ class Ticket_test : public beast::unit_test::suite { // Create tickets on a non-existent account. Env env{*this}; - Account alice{"alice"}; + Account const alice{"alice"}; env.memoize(alice); env(ticket::create(alice, 1), json(jss::Sequence, 1), ter(terNO_ACCOUNT)); @@ -383,11 +387,11 @@ class Ticket_test : public beast::unit_test::suite // Exceed the threshold where tickets can no longer be // added to an account. Env env{*this}; - Account alice{"alice"}; + Account const alice{"alice"}; env.fund(XRP(100000), alice); - std::uint32_t ticketSeq{env.seq(alice) + 1}; + std::uint32_t const ticketSeq{env.seq(alice) + 1}; env(ticket::create(alice, 250)); checkTicketCreateMeta(env); env.close(); @@ -420,12 +424,12 @@ class Ticket_test : public beast::unit_test::suite { // Explore exceeding the ticket threshold from another angle. Env env{*this}; - Account alice{"alice"}; + Account const alice{"alice"}; env.fund(XRP(100000), alice); env.close(); - std::uint32_t ticketSeq_AB{env.seq(alice) + 1}; + std::uint32_t const ticketSeq_AB{env.seq(alice) + 1}; env(ticket::create(alice, 2)); checkTicketCreateMeta(env); env.close(); @@ -458,7 +462,7 @@ class Ticket_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this}; - Account alice{"alice"}; + Account const alice{"alice"}; // Fund alice not quite enough to make the reserve for a Ticket. env.fund(env.current()->fees().accountReserve(1) - drops(1), alice); @@ -511,7 +515,7 @@ class Ticket_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this}; - Account alice{"alice"}; + Account const alice{"alice"}; env.fund(XRP(10000), alice); env.close(); @@ -607,14 +611,14 @@ class Ticket_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this}; - Account alice{"alice"}; + Account const alice{"alice"}; env.fund(XRP(10000), alice); env.close(); // Lambda that returns the hash of the most recent transaction. auto getTxID = [&env, this]() -> uint256 { - std::shared_ptr tx{env.tx()}; + std::shared_ptr const tx{env.tx()}; if (!BEAST_EXPECTS(tx, "Transaction not found")) Throw("Invalid transaction ID"); @@ -685,7 +689,7 @@ class Ticket_test : public beast::unit_test::suite BEAST_EXPECT(txErrCode == rpcSUCCESS); if (auto txPtr = std::get_if(&maybeTx)) { - std::shared_ptr& tx = txPtr->first; + std::shared_ptr const& tx = txPtr->first; BEAST_EXPECT(tx->getLedger() == ledgerSeq); std::shared_ptr const& sttx = tx->getSTransaction(); BEAST_EXPECT((*sttx)[sfSequence] == txSeq); @@ -722,7 +726,7 @@ class Ticket_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this}; - Account alice{"alice"}; + Account const alice{"alice"}; env.fund(XRP(10000), alice); env.close(); @@ -808,7 +812,7 @@ class Ticket_test : public beast::unit_test::suite testcase("Fix both Seq and Ticket"); Env env{*this, testable_amendments()}; - Account alice{"alice"}; + Account const alice{"alice"}; env.fund(XRP(10000), alice); env.close(); diff --git a/src/test/app/Transaction_ordering_test.cpp b/src/test/app/Transaction_ordering_test.cpp index f7d431fc90..c50fbf4e56 100644 --- a/src/test/app/Transaction_ordering_test.cpp +++ b/src/test/app/Transaction_ordering_test.cpp @@ -95,14 +95,16 @@ struct Transaction_ordering_test : public beast::unit_test::suite env.fund(XRP(1000), noripple(alice)); auto const aliceSequence = env.seq(alice); + static constexpr auto kSIZE = 5; std::vector tx; - for (auto i = 0; i < 5; ++i) + tx.reserve(kSIZE); + for (auto i = 0; i < kSIZE; ++i) { tx.emplace_back(env.jt(noop(alice), seq(aliceSequence + i), last_ledger_seq(7))); } - for (auto i = 1; i < 5; ++i) + for (auto i = 1; i < kSIZE; ++i) { env(tx[i], ter(terPRE_SEQ)); BEAST_EXPECT(env.seq(alice) == aliceSequence); @@ -110,11 +112,11 @@ struct Transaction_ordering_test : public beast::unit_test::suite env(tx[0]); env.app().getJobQueue().rendezvous(); - BEAST_EXPECT(env.seq(alice) == aliceSequence + 5); + BEAST_EXPECT(env.seq(alice) == aliceSequence + kSIZE); env.close(); - for (auto i = 0; i < 5; ++i) + for (auto i = 0; i < kSIZE; ++i) { auto const result = env.rpc("tx", to_string(tx[i].stx->getTransactionID())); BEAST_EXPECT(result["result"]["meta"]["TransactionResult"] == "tesSUCCESS"); diff --git a/src/test/app/TrustAndBalance_test.cpp b/src/test/app/TrustAndBalance_test.cpp index 413ef6dd78..a71ddb140e 100644 --- a/src/test/app/TrustAndBalance_test.cpp +++ b/src/test/app/TrustAndBalance_test.cpp @@ -28,7 +28,7 @@ class TrustAndBalance_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this}; - Account alice{"alice"}; + Account const alice{"alice"}; env(trust(env.master, alice["USD"](100)), ter(tecNO_DST)); } @@ -40,9 +40,9 @@ class TrustAndBalance_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this}; - Account gw{"gateway"}; - Account alice{"alice"}; - Account bob{"bob"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(10000), gw, alice, bob); env.close(); @@ -112,8 +112,8 @@ class TrustAndBalance_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this, features}; - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(10000), alice, bob); env.close(); @@ -156,9 +156,9 @@ class TrustAndBalance_test : public beast::unit_test::suite Env env{*this, features}; auto wsc = test::makeWSClient(env.app().config()); - Account gw{"gateway"}; - Account alice{"alice"}; - Account bob{"bob"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(10000), gw, alice, bob); env.close(); @@ -229,9 +229,9 @@ class TrustAndBalance_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this, features}; - Account gw{"gateway"}; - Account alice{"alice"}; - Account bob{"bob"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(10000), gw, alice, bob); env.close(); @@ -276,9 +276,9 @@ class TrustAndBalance_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this, features}; - Account gw{"gateway"}; - Account alice{"alice"}; - Account bob{"bob"}; + Account const gw{"gateway"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(10000), gw, alice, bob); env.close(); @@ -319,11 +319,11 @@ class TrustAndBalance_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this, features}; - Account gw{"gateway"}; - Account amazon{"amazon"}; - Account alice{"alice"}; - Account bob{"bob"}; - Account carol{"carol"}; + Account const gw{"gateway"}; + Account const amazon{"amazon"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; env.fund(XRP(10000), gw, amazon, alice, bob, carol); env.close(); @@ -343,12 +343,16 @@ class TrustAndBalance_test : public beast::unit_test::suite // alice pays amazon via multiple paths if (with_rate) + { env(pay(alice, amazon, gw["USD"](150)), sendmax(alice["USD"](200)), test::jtx::path(bob), test::jtx::path(carol)); + } else + { env(pay(alice, amazon, gw["USD"](150)), test::jtx::path(bob), test::jtx::path(carol)); + } if (with_rate) { @@ -375,7 +379,7 @@ class TrustAndBalance_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this, features}; - Account alice{"alice"}; + Account const alice{"alice"}; auto wsc = test::makeWSClient(env.app().config()); env.fund(XRP(10000), alice); diff --git a/src/test/app/SetTrust_test.cpp b/src/test/app/TrustSet_test.cpp similarity index 97% rename from src/test/app/SetTrust_test.cpp rename to src/test/app/TrustSet_test.cpp index 06aebcd915..d359ba1f26 100644 --- a/src/test/app/SetTrust_test.cpp +++ b/src/test/app/TrustSet_test.cpp @@ -7,7 +7,7 @@ namespace xrpl { namespace test { -class SetTrust_test : public beast::unit_test::suite +class TrustSet_test : public beast::unit_test::suite { public: void @@ -151,9 +151,13 @@ public: testFreeTrustlines(FeatureBitset features, bool thirdLineCreatesLE, bool createOnHighAcct) { if (thirdLineCreatesLE) + { testcase("Allow two free trustlines"); + } else + { testcase("Dynamic reserve for trustline"); + } using namespace jtx; Env env(*this, features); @@ -223,9 +227,9 @@ public: } void - testTicketSetTrust(FeatureBitset features) + testTicketTrustSet(FeatureBitset features) { - testcase("SetTrust using a ticket"); + testcase("TrustSet using a ticket"); using namespace jtx; @@ -256,7 +260,7 @@ public: env.close(); } - Json::Value + static Json::Value trust_explicit_amt(jtx::Account const& a, STAmount const& amt) { Json::Value jv; @@ -270,7 +274,7 @@ public: void testMalformedTransaction(FeatureBitset features) { - testcase("SetTrust checks for malformed transactions"); + testcase("TrustSet checks for malformed transactions"); using namespace jtx; Env env{*this, features}; @@ -283,9 +287,11 @@ public: for (std::uint64_t badFlag = 1u; badFlag <= std::numeric_limits::max(); badFlag *= 2) { - if (badFlag & tfTrustSetMask) + if ((badFlag & tfTrustSetMask) != 0u) + { env(trust(alice, gw["USD"](100), static_cast(badFlag)), ter(temINVALID_FLAG)); + } } // trust amount can't be XRP @@ -405,7 +411,7 @@ public: void testModifyQualityOfTrustline(FeatureBitset features, bool createQuality, bool createOnHighAcct) { - testcase << "SetTrust " << (createQuality ? "creates" : "removes") + testcase << "TrustSet " << (createQuality ? "creates" : "removes") << " quality of trustline for " << (createOnHighAcct ? "high" : "low") << " account"; @@ -570,7 +576,7 @@ public: // true, true case doesn't matter since creating a trustline ledger // entry requires reserve from the creator // independent of hi/low account ids for endpoints - testTicketSetTrust(features); + testTicketTrustSet(features); testMalformedTransaction(features); testModifyQualityOfTrustline(features, false, false); testModifyQualityOfTrustline(features, false, true); @@ -593,6 +599,6 @@ public: testWithFeats(sa); } }; -BEAST_DEFINE_TESTSUITE(SetTrust, app, xrpl); +BEAST_DEFINE_TESTSUITE(TrustSet, app, xrpl); } // namespace test } // namespace xrpl diff --git a/src/test/app/TxQ_test.cpp b/src/test/app/TxQ_test.cpp index 94db50842a..6f13f9d419 100644 --- a/src/test/app/TxQ_test.cpp +++ b/src/test/app/TxQ_test.cpp @@ -24,7 +24,7 @@ class TxQPosNegFlows_test : public beast::unit_test::suite static constexpr FeeLevel64 baseFeeLevel{256}; static constexpr FeeLevel64 minEscalationFeeLevel = baseFeeLevel * 500; - void + static void fillQueue(jtx::Env& env, jtx::Account const& account) { auto metrics = env.app().getTxQ().getMetrics(*env.current()); @@ -32,7 +32,7 @@ class TxQPosNegFlows_test : public beast::unit_test::suite env(noop(account)); } - auto + static auto openLedgerCost(jtx::Env& env) { using namespace jtx; @@ -52,7 +52,7 @@ class TxQPosNegFlows_test : public beast::unit_test::suite // Get a fee level of a transaction made by an account // This fee level is used to ensure we can place transaction into TxQ - auto + static auto txFeeLevelByAccount(jtx::Env& env, jtx::Account const& account) { using namespace jtx; @@ -65,7 +65,7 @@ class TxQPosNegFlows_test : public beast::unit_test::suite // Calculating expected median fee level based on known fee levels of median // transaction levels. - auto + static auto calcMedFeeLevel(FeeLevel64 const feeLevel1, FeeLevel64 const feeLevel2) { FeeLevel64 const expectedMedFeeLevel = (feeLevel1 + feeLevel2 + FeeLevel64{1}) / 2; @@ -73,7 +73,7 @@ class TxQPosNegFlows_test : public beast::unit_test::suite return std::max(expectedMedFeeLevel, minEscalationFeeLevel).fee(); } - auto + static auto calcMedFeeLevel(FeeLevel64 const feeLevel) { return calcMedFeeLevel(feeLevel, feeLevel); @@ -261,9 +261,7 @@ public: env(noop(alice), fee(baseFee * 2.0), queued); // Queue is full now. - // clang-format off checkMetrics(*this, env, 6, 6, 4, 3, txFeeLevelByAccount(env, daria) + 1); - // clang-format on // Try to add another transaction with the default (low) fee, // it should fail because the queue is full. env(noop(charlie), ter(telCAN_NOT_QUEUE_FULL)); @@ -274,18 +272,22 @@ public: env(noop(charlie), fee(baseFee * 10), queued); // Queue is still full, of course, but the min fee has gone up - // clang-format off checkMetrics(*this, env, 6, 6, 4, 3, txFeeLevelByAccount(env, elmo) + 1); - // clang-format on // Close out the ledger, the transactions are accepted, the // queue is cleared, then the localTxs are retried. At this // point, daria's transaction that was dropped from the queue // is put back in. Neat. env.close(); - // clang-format off - checkMetrics(*this, env, 2, 8, 5, 4, baseFeeLevel.fee(), calcMedFeeLevel(FeeLevel64{baseFeeLevel.fee() * largeFeeMultiplier})); - // clang-format on + checkMetrics( + *this, + env, + 2, + 8, + 5, + 4, + baseFeeLevel.fee(), + calcMedFeeLevel(FeeLevel64{baseFeeLevel.fee() * largeFeeMultiplier})); env.close(); checkMetrics(*this, env, 0, 10, 2, 5); @@ -498,7 +500,7 @@ public: // We haven't yet shown that ticket-based transactions can be added // to the queue in any order. We should do that... - std::uint32_t tkt250 = tkt1 + 249; + std::uint32_t const tkt250 = tkt1 + 249; env(noop(alice), ticket::use(tkt250 - 0), fee(baseFee * 3.0), queued); env(noop(alice), ticket::use(tkt1 + 14), fee(baseFee * 2.9), queued); env(noop(alice), ticket::use(tkt250 - 1), fee(baseFee * 2.8), queued); @@ -551,16 +553,16 @@ public: // The lowest fee ticket is baseFee * 2.1, trying to replace it env(noop(alice), ticket::use(tkt1 + 18), - fee(baseFee * 2.1 * 1.25 - 1), + fee((baseFee * 2.1 * 1.25) - 1), ter(telCAN_NOT_QUEUE_FEE)); - env(noop(alice), ticket::use(tkt1 + 18), fee(baseFee * 2.1 * 1.25 + 1), queued); + env(noop(alice), ticket::use(tkt1 + 18), fee((baseFee * 2.1 * 1.25) + 1), queued); // New lowest fee ticket is baseFee * 2.2 env(noop(alice), ticket::use(tkt250 - 4), - fee(baseFee * 2.2 * 1.25 - 1), + fee((baseFee * 2.2 * 1.25) - 1), ter(telCAN_NOT_QUEUE_FEE)); - env(noop(alice), ticket::use(tkt250 - 4), fee(baseFee * 2.2 * 1.25 + 1), queued); + env(noop(alice), ticket::use(tkt250 - 4), fee((baseFee * 2.2 * 1.25) + 1), queued); env.close(); env.require(owners(alice, 227), tickets(alice, 227)); @@ -722,6 +724,7 @@ public: auto aliceStat = txQ.getAccountTxs(alice.id()); BEAST_EXPECT(aliceStat.size() == 1); BEAST_EXPECT(aliceStat.begin()->feeLevel == baseFeeLevel); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(aliceStat.begin()->lastValid && *aliceStat.begin()->lastValid == 8); BEAST_EXPECT(!aliceStat.begin()->consequences.isBlocker()); @@ -749,15 +752,13 @@ public: env.close(); // alice's transaction is still hanging around - // clang-format off - checkMetrics(*this, env, 1, 8, 5, 4, baseFeeLevel.fee(), baseFeeLevel.fee() * largeFeeMultiplier); - // clang-format on + checkMetrics( + *this, env, 1, 8, 5, 4, baseFeeLevel.fee(), baseFeeLevel.fee() * largeFeeMultiplier); BEAST_EXPECT(env.seq(alice) == 3); constexpr auto anotherLargeFeeMultiplier = 800; auto const anotherLargeFee = baseFee * anotherLargeFeeMultiplier; // Keep alice's transaction waiting. - // clang-format off env(noop(bob), fee(anotherLargeFee), queued); env(noop(charlie), fee(anotherLargeFee), queued); env(noop(daria), fee(anotherLargeFee), queued); @@ -765,24 +766,36 @@ public: env(noop(edgar), fee(anotherLargeFee), queued); env(noop(felicia), fee(anotherLargeFee - 1), queued); env(noop(felicia), fee(anotherLargeFee - 1), seq(env.seq(felicia) + 1), queued); - checkMetrics(*this, env, 8, 8, 5, 4, baseFeeLevel.fee() + 1, baseFeeLevel.fee() * largeFeeMultiplier); - // clang-format on + checkMetrics( + *this, + env, + 8, + 8, + 5, + 4, + baseFeeLevel.fee() + 1, + baseFeeLevel.fee() * largeFeeMultiplier); env.close(); // alice's transaction expired without getting // into the ledger, so her transaction is gone, // though one of felicia's is still in the queue. - // clang-format off - checkMetrics(*this, env, 1, 10, 6, 5, baseFeeLevel.fee(), baseFeeLevel.fee() * largeFeeMultiplier); - // clang-format on + checkMetrics( + *this, env, 1, 10, 6, 5, baseFeeLevel.fee(), baseFeeLevel.fee() * largeFeeMultiplier); BEAST_EXPECT(env.seq(alice) == 3); BEAST_EXPECT(env.seq(felicia) == 7); env.close(); // And now the queue is empty - // clang-format off - checkMetrics(*this, env, 0, 12, 1, 6, baseFeeLevel.fee(), baseFeeLevel.fee() * anotherLargeFeeMultiplier); - // clang-format on + checkMetrics( + *this, + env, + 0, + 12, + 1, + 6, + baseFeeLevel.fee(), + baseFeeLevel.fee() * anotherLargeFeeMultiplier); BEAST_EXPECT(env.seq(alice) == 3); BEAST_EXPECT(env.seq(felicia) == 8); } @@ -856,9 +869,7 @@ public: feeCarol = (feeCarol + 1) * 125 / 100; ++seqCarol; } - // clang-format off - checkMetrics(*this, env, 6, 6, 4, 3, baseFeeLevel.fee() * aliceFeeMultiplier + 1); - // clang-format on + checkMetrics(*this, env, 6, 6, 4, 3, (baseFeeLevel.fee() * aliceFeeMultiplier) + 1); // Carol submits high enough to beat Bob's average fee which kicks // out Bob's queued transaction. However Bob's transaction stays @@ -949,7 +960,7 @@ public: Env::ParsedResult parsed; - env.app().openLedger().modify([&](OpenView& view, beast::Journal j) { + env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) { auto const result = xrpl::apply(env.app(), view, *jt.stx, tapNONE, env.journal); parsed.ter = result.ter; return result.applied; @@ -1504,14 +1515,12 @@ public: env.close(); // If not for the maximum, the per ledger would be 11. - // clang-format off checkMetrics(*this, env, 0, 10, 0, 5, baseFeeLevel.fee(), calcMedFeeLevel(medFeeLevel)); - // clang-format on } try { - Env env( + Env const env( *this, makeConfig( {{"minimum_txn_in_ledger", "200"}, @@ -1532,7 +1541,7 @@ public: } try { - Env env( + Env const env( *this, makeConfig( {{"minimum_txn_in_ledger", "200"}, @@ -1553,7 +1562,7 @@ public: } try { - Env env( + Env const env( *this, makeConfig( {{"minimum_txn_in_ledger", "2"}, @@ -2309,7 +2318,7 @@ public: auto const jtx = env.jt(offer_cancel(alice, 3), seq(5), fee(10)); auto const pf = preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); - BEAST_EXPECT(pf.ter == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(10)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0)); @@ -2321,7 +2330,7 @@ public: auto const jtx = env.jt(trust("carol", USD(50000000)), seq(1), fee(10)); auto const pf = preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); - BEAST_EXPECT(pf.ter == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(10)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0)); @@ -2331,7 +2340,7 @@ public: auto const jtx = env.jt(ticket::create(alice, 1), seq(1), fee(10)); auto const pf = preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); - BEAST_EXPECT(pf.ter == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(10)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0)); @@ -2631,7 +2640,7 @@ public: // Start by procuring tickets for alice to use to keep her queue full // without affecting the sequence gap that will appear later. - env(ticket::create(alice, 11), seq(aliceSeq + 0), fee(baseFee * 20 + 1), ter(terQUEUED)); + env(ticket::create(alice, 11), seq(aliceSeq + 0), fee((baseFee * 20) + 1), ter(terQUEUED)); env(noop(alice), seq(aliceSeq + 11), last_ledger_seq(11), ter(terQUEUED)); env(noop(alice), seq(aliceSeq + 12), last_ledger_seq(11), ter(terQUEUED)); env(noop(alice), seq(aliceSeq + 13), last_ledger_seq(11), ter(terQUEUED)); @@ -2756,14 +2765,18 @@ public: for (int i = 0; i < 5; ++i) { if (i == 2) + { envs( noop(alice), fee(baseFee * 100), seq(none), json(jss::LastLedgerSequence, lastLedgerSeq), ter(terQUEUED))(submitParams); + } else + { envs(noop(alice), fee(baseFee * 100), seq(none), ter(terQUEUED))(submitParams); + } } checkMetrics(*this, env, 5, std::nullopt, 7, 6); { @@ -3374,6 +3387,7 @@ public: BEAST_EXPECT(jv[jss::status] == "success"); } + // NOLINTNEXTLINE(misc-const-correctness) Account a{"a"}, b{"b"}, c{"c"}, d{"d"}, e{"e"}, f{"f"}, g{"g"}, h{"h"}, i{"i"}; // Fund the first few accounts at non escalated fee @@ -3515,7 +3529,8 @@ public: } auto const den = (metrics.txPerLedger * metrics.txPerLedger); - FeeLevel64 feeLevel = (metrics.medFeeLevel * totalFactor + FeeLevel64{den - 1}) / den; + FeeLevel64 const feeLevel = + (metrics.medFeeLevel * totalFactor + FeeLevel64{den - 1}) / den; auto result = toDrops(feeLevel, env.current()->fees().base).drops(); @@ -3868,10 +3883,10 @@ public: // (This requires calling directly into the open ledger, // which won't work if unit tests are separated to only // be callable via RPC.) - env.app().openLedger().modify([&](OpenView& view, beast::Journal j) { + env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) { auto const tx = env.jt(noop(alice), seq(aliceSeq), fee(openLedgerCost(env))); auto const result = xrpl::apply(env.app(), view, *tx.stx, tapUNLIMITED, j); - BEAST_EXPECT(result.ter == tesSUCCESS && result.applied); + BEAST_EXPECT(isTesSuccess(result.ter) && result.applied); return result.applied; }); // the queued transaction is still there @@ -3936,10 +3951,10 @@ public: // (This requires calling directly into the open ledger, // which won't work if unit tests are separated to only // be callable via RPC.) - env.app().openLedger().modify([&](OpenView& view, beast::Journal j) { + env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) { auto const tx = env.jt(noop(alice), ticket::use(tktSeq0 + 1), fee(openLedgerCost(env))); auto const result = xrpl::apply(env.app(), view, *tx.stx, tapUNLIMITED, j); - BEAST_EXPECT(result.ter == tesSUCCESS && result.applied); + BEAST_EXPECT(isTesSuccess(result.ter) && result.applied); return result.applied; }); // the queued transaction is still there @@ -4078,7 +4093,7 @@ public: // Use fees to guarantee order int txFee{static_cast(baseFee * 9)}; auto prepareFee = [&](uint64_t multiplier) { - return fee(txFee - multiplier * baseFee / 10); + return fee(txFee - (multiplier * baseFee / 10)); }; uint64_t multiplier = 0; diff --git a/src/test/app/ValidatorKeys_test.cpp b/src/test/app/ValidatorKeys_test.cpp index 925c02131b..3be6fcd028 100644 --- a/src/test/app/ValidatorKeys_test.cpp +++ b/src/test/app/ValidatorKeys_test.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -58,27 +59,31 @@ public: // We're only using Env for its Journal. That Journal gives better // coverage in unit tests. test::jtx::Env env{*this, test::jtx::envconfig(), nullptr, beast::severities::kDisabled}; - beast::Journal journal{env.app().journal("ValidatorKeys_test")}; + beast::Journal const journal{env.app().getJournal("ValidatorKeys_test")}; // Keys/ID when using [validation_seed] SecretKey const seedSecretKey = + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) generateSecretKey(KeyType::secp256k1, *parseBase58(seed)); PublicKey const seedPublicKey = derivePublicKey(KeyType::secp256k1, seedSecretKey); NodeID const seedNodeID = calcNodeID(seedPublicKey); // Keys when using [validation_token] + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const tokenSecretKey = *parseBase58(TokenType::NodePrivate, tokenSecretStr); auto const tokenPublicKey = derivePublicKey(KeyType::secp256k1, tokenSecretKey); auto const m = deserializeManifest(base64_decode(tokenManifest)); BEAST_EXPECT(m); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) NodeID const tokenNodeID = calcNodeID(m->masterKey); { // No config -> no key but valid - Config c; - ValidatorKeys k{c, journal}; + Config const c; + ValidatorKeys const k{c, journal}; BEAST_EXPECT(!k.keys); BEAST_EXPECT(k.manifest.empty()); BEAST_EXPECT(!k.configInvalid()); @@ -89,10 +94,10 @@ public: c.section(SECTION_VALIDATION_SEED).append(seed); ValidatorKeys k{c, journal}; - if (BEAST_EXPECT(k.keys)) + if (BEAST_EXPECT(k.keys); k.keys.has_value()) { BEAST_EXPECT(k.keys->publicKey == seedPublicKey); - BEAST_EXPECT(k.keys->secretKey == seedSecretKey); + BEAST_EXPECT(test::equal(k.keys->secretKey, seedSecretKey)); } BEAST_EXPECT(k.nodeID == seedNodeID); BEAST_EXPECT(k.manifest.empty()); @@ -104,7 +109,7 @@ public: Config c; c.section(SECTION_VALIDATION_SEED).append("badseed"); - ValidatorKeys k{c, journal}; + ValidatorKeys const k{c, journal}; BEAST_EXPECT(k.configInvalid()); BEAST_EXPECT(!k.keys); BEAST_EXPECT(k.manifest.empty()); @@ -116,10 +121,10 @@ public: c.section(SECTION_VALIDATOR_TOKEN).append(tokenBlob); ValidatorKeys k{c, journal}; - if (BEAST_EXPECT(k.keys)) + if (BEAST_EXPECT(k.keys); k.keys.has_value()) { BEAST_EXPECT(k.keys->publicKey == tokenPublicKey); - BEAST_EXPECT(k.keys->secretKey == tokenSecretKey); + BEAST_EXPECT(test::equal(k.keys->secretKey, tokenSecretKey)); } BEAST_EXPECT(k.nodeID == tokenNodeID); BEAST_EXPECT(k.manifest == tokenManifest); @@ -129,7 +134,7 @@ public: // invalid validator token Config c; c.section(SECTION_VALIDATOR_TOKEN).append("badtoken"); - ValidatorKeys k{c, journal}; + ValidatorKeys const k{c, journal}; BEAST_EXPECT(k.configInvalid()); BEAST_EXPECT(!k.keys); BEAST_EXPECT(k.manifest.empty()); @@ -140,7 +145,7 @@ public: Config c; c.section(SECTION_VALIDATION_SEED).append(seed); c.section(SECTION_VALIDATOR_TOKEN).append(tokenBlob); - ValidatorKeys k{c, journal}; + ValidatorKeys const k{c, journal}; BEAST_EXPECT(k.configInvalid()); BEAST_EXPECT(!k.keys); @@ -151,7 +156,7 @@ public: // Token manifest and private key must match Config c; c.section(SECTION_VALIDATOR_TOKEN).append(invalidTokenBlob); - ValidatorKeys k{c, journal}; + ValidatorKeys const k{c, journal}; BEAST_EXPECT(k.configInvalid()); BEAST_EXPECT(!k.keys); diff --git a/src/test/app/ValidatorList_test.cpp b/src/test/app/ValidatorList_test.cpp index c85a508fb9..74b208e5e2 100644 --- a/src/test/app/ValidatorList_test.cpp +++ b/src/test/app/ValidatorList_test.cpp @@ -56,9 +56,11 @@ private: if (seq != std::numeric_limits::max()) { st[sfSigningPubKey] = spk; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) sign(st, HashPrefix::manifest, *publicKeyType(spk), ssk); } + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) sign(st, HashPrefix::manifest, *publicKeyType(pk), sk, sfMasterSignature); Serializer s; @@ -74,6 +76,7 @@ private: st[sfSequence] = std::numeric_limits::max(); st[sfPublicKey] = pk; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) sign(st, HashPrefix::manifest, *publicKeyType(pk), sk, sfMasterSignature); Serializer s; @@ -95,7 +98,7 @@ private: masterPublic, secret, signingKeys.first, signingKeys.second, 1))}; } - std::string + static std::string makeList( std::vector const& validators, std::size_t sequence, @@ -119,7 +122,7 @@ private: return base64_encode(data); } - std::string + static std::string signList(std::string const& blob, std::pair const& keys) { auto const data = base64_decode(blob); @@ -242,6 +245,7 @@ private: trustedKeys->load(localSigningPublicOuter, emptyCfgKeys, emptyCfgPublishers)); BEAST_EXPECT(trustedKeys->listed(localSigningPublicOuter)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) manifests.applyManifest(*deserializeManifest(cfgManifest)); BEAST_EXPECT( trustedKeys->load(localSigningPublicOuter, emptyCfgKeys, emptyCfgPublishers)); @@ -268,7 +272,7 @@ private: auto const masterNode1 = randomMasterKey(); auto const masterNode2 = randomMasterKey(); - std::vector cfgMasterKeys( + std::vector const cfgMasterKeys( {format(masterNode1), format(masterNode2, " Comment")}); BEAST_EXPECT(trustedKeys->load({}, cfgMasterKeys, emptyCfgPublishers)); BEAST_EXPECT(trustedKeys->listed(masterNode1)); @@ -298,8 +302,8 @@ private: parseBase58(TokenType::NodePublic, cfgKeys.front()); BEAST_EXPECT(trustedKeys->load(localSigningPublic, cfgKeys, emptyCfgPublishers)); - BEAST_EXPECT(trustedKeys->localPublicKey() == localSigningPublic); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(trustedKeys->listed(*localSigningPublic)); for (auto const& n : configList) BEAST_EXPECT(trustedKeys->listed(n)); @@ -332,6 +336,7 @@ private: app.config().legacy("database_path"), env.journal); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) manifests.applyManifest(*deserializeManifest(cfgManifest)); BEAST_EXPECT(trustedKeys->load(localSigningPublicOuter, cfgKeys, emptyCfgPublishers)); @@ -357,7 +362,8 @@ private: // load should reject validator list signing keys with invalid // encoding - std::vector keys({randomMasterKey(), randomMasterKey(), randomMasterKey()}); + std::vector const keys( + {randomMasterKey(), randomMasterKey(), randomMasterKey()}); badPublishers.clear(); for (auto const& key : keys) badPublishers.push_back(toBase58(TokenType::NodePublic, key)); @@ -368,13 +374,14 @@ private: // load should accept valid validator list publisher keys std::vector cfgPublishers; + cfgPublishers.reserve(keys.size()); for (auto const& key : keys) cfgPublishers.push_back(strHex(key)); BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublishers)); for (auto const& key : keys) BEAST_EXPECT(trustedKeys->trustedPublisher(key)); - BEAST_EXPECT(trustedKeys->getListThreshold() == keys.size() / 2 + 1); + BEAST_EXPECT(trustedKeys->getListThreshold() == (keys.size() / 2) + 1); } { ManifestCache manifests; @@ -385,9 +392,10 @@ private: app.config().legacy("database_path"), env.journal); - std::vector keys( + std::vector const keys( {randomMasterKey(), randomMasterKey(), randomMasterKey(), randomMasterKey()}); std::vector cfgPublishers; + cfgPublishers.reserve(keys.size()); for (auto const& key : keys) cfgPublishers.push_back(strHex(key)); @@ -414,6 +422,7 @@ private: auto const pubRevokedSigning = randomKeyPair(KeyType::secp256k1); // make this manifest revoked (seq num = max) // -- thus should not be loaded + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) pubManifests.applyManifest(*deserializeManifest(makeManifestString( pubRevokedPublic, pubRevokedSecret, @@ -425,7 +434,7 @@ private: auto legitKey1 = randomMasterKey(); auto legitKey2 = randomMasterKey(); - std::vector cfgPublishers = { + std::vector const cfgPublishers = { strHex(pubRevokedPublic), strHex(legitKey1), strHex(legitKey2)}; BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublishers)); @@ -452,6 +461,7 @@ private: auto const pubRevokedSigning = randomKeyPair(KeyType::secp256k1); // make this manifest revoked (seq num = max) // -- thus should not be loaded + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) pubManifests.applyManifest(*deserializeManifest(makeManifestString( pubRevokedPublic, pubRevokedSecret, @@ -462,7 +472,8 @@ private: // this one is not revoked (and not in the manifest cache at all.) auto legitKey = randomMasterKey(); - std::vector cfgPublishers = {strHex(pubRevokedPublic), strHex(legitKey)}; + std::vector const cfgPublishers = { + strHex(pubRevokedPublic), strHex(legitKey)}; BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublishers, std::size_t(2))); BEAST_EXPECT(!trustedKeys->trustedPublisher(pubRevokedPublic)); @@ -527,7 +538,7 @@ private: auto trustedKeys = std::make_unique( manifests, manifests, - env.app().timeKeeper(), + env.app().getTimeKeeper(), app.config().legacy("database_path"), env.journal); @@ -554,10 +565,10 @@ private: auto const manifest1 = base64_encode(makeManifestString( publisherPublic, publisherSecret, pubSigningKeys1.first, pubSigningKeys1.second, 1)); - std::vector cfgKeys1({strHex(publisherPublic)}); - std::vector emptyCfgKeys; + std::vector const cfgPublisherKeys({strHex(publisherPublic)}); + std::vector const emptyCfgKeys; - BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgKeys1)); + BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublisherKeys)); std::map> const lists = []() { auto constexpr listSize = 20; @@ -806,7 +817,7 @@ private: {}, effective6 + 1s, env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); expectUntrusted(lists.at(3)); @@ -824,7 +835,7 @@ private: {}, effective8 + 1s, env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); expectUntrusted(lists.at(6)); @@ -889,7 +900,7 @@ private: auto trustedKeys = std::make_unique( manifests, manifests, - env.app().timeKeeper(), + env.app().getTimeKeeper(), app.config().legacy("database_path"), env.journal); @@ -900,10 +911,10 @@ private: auto const manifest = base64_encode(makeManifestString( publisherPublic, publisherSecret, pubSigningKeys1.first, pubSigningKeys1.second, 1)); - std::vector cfgKeys1({strHex(publisherPublic)}); - std::vector emptyCfgKeys; + std::vector const cfgPublisherKeys({strHex(publisherPublic)}); + std::vector const emptyCfgKeys; - BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgKeys1)); + BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublisherKeys)); std::vector const list = []() { auto constexpr listSize = 20; @@ -950,6 +961,7 @@ private: auto const available = trustedKeys->getAvailable(hexPublic, 0); if (BEAST_EXPECT(available)) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& a = *available; BEAST_EXPECT(!a); } @@ -959,6 +971,7 @@ private: auto const available = trustedKeys->getAvailable(hexPublic, 3); if (BEAST_EXPECT(available)) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& a = *available; BEAST_EXPECT(!a); } @@ -968,6 +981,7 @@ private: auto const available = trustedKeys->getAvailable(hexPublic, 1); if (BEAST_EXPECT(available)) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& a = *available; BEAST_EXPECT(a[jss::public_key] == hexPublic); BEAST_EXPECT(a[jss::manifest] == manifest); @@ -984,6 +998,7 @@ private: auto const available = trustedKeys->getAvailable(hexPublic, 2); if (BEAST_EXPECT(available)) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& a = *available; BEAST_EXPECT(a[jss::public_key] == hexPublic); BEAST_EXPECT(a[jss::manifest] == manifest); @@ -1020,7 +1035,7 @@ private: app.config().legacy("database_path"), env.journal); - std::vector cfgPublishersOuter; + std::vector const cfgPublishersOuter; hash_set activeValidatorsOuter; std::size_t const maxKeys = 40; @@ -1034,9 +1049,13 @@ private: auto const valKey = randomNode(); cfgKeys.push_back(toBase58(TokenType::NodePublic, valKey)); if (cfgKeys.size() <= maxKeys - 5) + { activeValidatorsOuter.emplace(calcNodeID(valKey)); + } else + { unseenValidators.emplace(calcNodeID(valKey)); + } } BEAST_EXPECT(trustedKeysOuter->load({}, cfgKeys, cfgPublishersOuter)); @@ -1047,7 +1066,7 @@ private: activeValidatorsOuter, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); for (auto const& val : unseenValidators) @@ -1064,14 +1083,16 @@ private: BEAST_EXPECT(trustedKeysOuter->trusted(*valKey)); } else + { fail(); + } } changes = trustedKeysOuter->updateTrusted( activeValidatorsOuter, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(changes.added.empty()); BEAST_EXPECT(changes.removed.empty()); @@ -1082,7 +1103,7 @@ private: auto const masterPrivate = randomSecretKey(); auto const masterPublic = derivePublicKey(KeyType::ed25519, masterPrivate); - std::vector cfgKeys({toBase58(TokenType::NodePublic, masterPublic)}); + std::vector const cfgKeys({toBase58(TokenType::NodePublic, masterPublic)}); BEAST_EXPECT(trustedKeysOuter->load({}, cfgKeys, cfgPublishersOuter)); @@ -1095,7 +1116,7 @@ private: activeValidatorsOuter, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(changes.added == asNodeIDs({masterPublic})); BEAST_EXPECT(changes.removed.empty()); @@ -1110,6 +1131,7 @@ private: masterPublic, masterPrivate, signingPublic1, signingKeys1.second, 1)); BEAST_EXPECT( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) manifestsOuter.applyManifest(std::move(*m1)) == ManifestDisposition::accepted); BEAST_EXPECT(trustedKeysOuter->listed(masterPublic)); BEAST_EXPECT(trustedKeysOuter->trusted(masterPublic)); @@ -1123,6 +1145,7 @@ private: auto m2 = deserializeManifest(makeManifestString( masterPublic, masterPrivate, signingPublic2, signingKeys2.second, 2)); BEAST_EXPECT( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) manifestsOuter.applyManifest(std::move(*m2)) == ManifestDisposition::accepted); BEAST_EXPECT(trustedKeysOuter->listed(masterPublic)); BEAST_EXPECT(trustedKeysOuter->trusted(masterPublic)); @@ -1137,9 +1160,12 @@ private: activeValidatorsOuter.emplace(calcNodeID(signingPublicMax)); auto mMax = deserializeManifest(makeRevocationString(masterPublic, masterPrivate)); + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(mMax->revoked()); BEAST_EXPECT( manifestsOuter.applyManifest(std::move(*mMax)) == ManifestDisposition::accepted); + // NOLINTEND(bugprone-unchecked-optional-access) + BEAST_EXPECT(manifestsOuter.getSigningKey(masterPublic) == masterPublic); BEAST_EXPECT(manifestsOuter.revoked(masterPublic)); @@ -1151,7 +1177,7 @@ private: activeValidatorsOuter, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(changes.removed == asNodeIDs({masterPublic})); BEAST_EXPECT(changes.added.empty()); @@ -1177,16 +1203,16 @@ private: auto const publisherSecret = randomSecretKey(); auto const publisherPublic = derivePublicKey(KeyType::ed25519, publisherSecret); - std::vector cfgPublishers({strHex(publisherPublic)}); - std::vector emptyCfgKeys; + std::vector const cfgPublishers({strHex(publisherPublic)}); + std::vector const emptyCfgKeys; BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublishers)); - TrustChanges changes = trustedKeys->updateTrusted( + TrustChanges const changes = trustedKeys->updateTrusted( activeValidatorsOuter, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(changes.removed.empty()); BEAST_EXPECT(changes.added.empty()); @@ -1203,22 +1229,22 @@ private: env.journal); auto const masterPrivate = randomSecretKey(); auto const masterPublic = derivePublicKey(KeyType::ed25519, masterPrivate); - std::vector cfgKeys({toBase58(TokenType::NodePublic, masterPublic)}); + std::vector const cfgKeys({toBase58(TokenType::NodePublic, masterPublic)}); auto const publisher1Secret = randomSecretKey(); auto const publisher1Public = derivePublicKey(KeyType::ed25519, publisher1Secret); auto const publisher2Secret = randomSecretKey(); auto const publisher2Public = derivePublicKey(KeyType::ed25519, publisher2Secret); - std::vector cfgPublishers( + std::vector const cfgPublishers( {strHex(publisher1Public), strHex(publisher2Public)}); BEAST_EXPECT(trustedKeys->load({}, cfgKeys, cfgPublishers, std::size_t(2))); - TrustChanges changes = trustedKeys->updateTrusted( + TrustChanges const changes = trustedKeys->updateTrusted( activeValidatorsOuter, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(changes.removed.empty()); BEAST_EXPECT(changes.added.size() == 1); @@ -1237,7 +1263,7 @@ private: env.journal, minQuorum); - std::size_t n = 10; + std::size_t const n = 10; std::vector cfgKeys; cfgKeys.reserve(n); hash_set expectedTrusted; @@ -1250,9 +1276,13 @@ private: cfgKeys.push_back(toBase58(TokenType::NodePublic, valKey)); expectedTrusted.emplace(calcNodeID(valKey)); if (cfgKeys.size() < std::ceil(n * 0.8f)) + { activeValidators.emplace(calcNodeID(valKey)); + } else if (cfgKeys.size() < std::ceil(n * 0.8f)) + { toBeSeen = calcNodeID(valKey); + } } BEAST_EXPECT(trustedKeys->load({}, cfgKeys, cfgPublishersOuter)); @@ -1261,7 +1291,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(changes.removed.empty()); BEAST_EXPECT(changes.added == expectedTrusted); @@ -1273,7 +1303,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(changes.removed.empty()); BEAST_EXPECT(changes.added.empty()); @@ -1284,11 +1314,11 @@ private: auto trustedKeys = std::make_unique( manifestsOuter, manifestsOuter, - env.app().timeKeeper(), + env.app().getTimeKeeper(), app.config().legacy("database_path"), env.journal); - std::vector emptyCfgKeys; + std::vector const emptyCfgKeys; auto const publisherKeys = randomKeyPair(KeyType::secp256k1); auto const pubSigningKeys = randomKeyPair(KeyType::secp256k1); auto const manifest = base64_encode(makeManifestString( @@ -1298,9 +1328,9 @@ private: pubSigningKeys.second, 1)); - std::vector cfgKeys({strHex(publisherKeys.first)}); + std::vector const cfgPublisherKeys({strHex(publisherKeys.first)}); - BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgKeys)); + BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublisherKeys)); std::vector list({randomValidator(), randomValidator()}); hash_set activeValidators( @@ -1323,7 +1353,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(changes.removed.empty()); BEAST_EXPECT(changes.added == activeValidators); @@ -1339,7 +1369,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(changes.removed == activeValidators); BEAST_EXPECT(changes.added.empty()); @@ -1364,7 +1394,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(changes.removed.empty()); BEAST_EXPECT( @@ -1387,7 +1417,7 @@ private: app.config().legacy("database_path"), env.journal); - std::vector cfgPublishers; + std::vector const cfgPublishers; hash_set activeValidators; hash_set activeKeys; @@ -1401,11 +1431,11 @@ private: activeValidators.emplace(calcNodeID(valKey)); activeKeys.emplace(valKey); BEAST_EXPECT(trustedKeys->load({}, cfgKeys, cfgPublishers)); - TrustChanges changes = trustedKeys->updateTrusted( + TrustChanges const changes = trustedKeys->updateTrusted( activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(changes.removed.empty()); BEAST_EXPECT(changes.added == asNodeIDs({valKey})); @@ -1424,7 +1454,7 @@ private: env.journal); auto const localKey = randomNode(); - std::vector cfgPublishers; + std::vector const cfgPublishers; hash_set activeValidators; hash_set activeKeys; std::vector cfgKeys{toBase58(TokenType::NodePublic, localKey)}; @@ -1438,17 +1468,21 @@ private: activeKeys.emplace(valKey); BEAST_EXPECT(trustedKeys->load(localKey, cfgKeys, cfgPublishers)); - TrustChanges changes = trustedKeys->updateTrusted( + TrustChanges const changes = trustedKeys->updateTrusted( activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(changes.removed.empty()); if (cfgKeys.size() > 2) + { BEAST_EXPECT(changes.added == asNodeIDs({valKey})); + } else + { BEAST_EXPECT(changes.added == asNodeIDs({localKey, valKey})); + } BEAST_EXPECT(trustedKeys->quorum() == std::ceil(cfgKeys.size() * 0.8f)); @@ -1500,8 +1534,8 @@ private: pubSigningKeys.second, 1)); - std::vector cfgPublishers({strHex(publisherPublic)}); - std::vector emptyCfgKeys; + std::vector const cfgPublishers({strHex(publisherPublic)}); + std::vector const emptyCfgKeys; // Threshold of 1 will result in a union of all the lists BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublishers, std::size_t(1))); @@ -1510,7 +1544,7 @@ private: auto const sequence = 1; using namespace std::chrono_literals; NetClock::time_point const validUntil = env.timeKeeper().now() + 3600s; - std::vector localKeys{locals[i].first, locals[i].second}; + std::vector const localKeys{locals[i].first, locals[i].second}; auto const blob = makeList(localKeys, sequence, validUntil.time_since_epoch().count()); auto const sig = signList(blob, pubSigningKeys); @@ -1526,11 +1560,11 @@ private: addPublishedList(i); BEAST_EXPECT(trustedKeys->getListThreshold() == 1); - TrustChanges changes = trustedKeys->updateTrusted( + TrustChanges const changes = trustedKeys->updateTrusted( activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(valKeys.size() * 0.8f)); @@ -1592,8 +1626,8 @@ private: pubSigningKeys.second, 1)); - std::vector cfgPublishers({strHex(publisherPublic)}); - std::vector emptyCfgKeys; + std::vector const cfgPublishers({strHex(publisherPublic)}); + std::vector const emptyCfgKeys; BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublishers)); @@ -1601,15 +1635,29 @@ private: auto const sequence = 1; using namespace std::chrono_literals; // Want to drop 1 sooner - NetClock::time_point const validUntil = env.timeKeeper().now() + - (i == 2 ? 120s - : i == 1 ? 60s - : 3600s); + std::chrono::seconds duration; + if (i == 2) + { + duration = 120s; + } + else if (i == 1) + { + duration = 60s; + } + else + { + duration = 3600s; + } + NetClock::time_point const validUntil = env.timeKeeper().now() + duration; if (i == 1) + { validUntil1 = validUntil; + } else if (i == 2) + { validUntil2 = validUntil; - std::vector localKeys{locals[i].first, locals[i].second}; + } + std::vector const localKeys{locals[i].first, locals[i].second}; auto const blob = makeList(localKeys, sequence, validUntil.time_since_epoch().count()); auto const sig = signList(blob, pubSigningKeys); @@ -1631,7 +1679,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil((valKeys.size() - 3) * 0.8f)); @@ -1649,7 +1697,9 @@ private: added.insert(calcNodeID(val.masterPublic)); } else + { BEAST_EXPECT(!trustedKeys->trusted(val.masterPublic)); + } } BEAST_EXPECT(changes.added == added); BEAST_EXPECT(changes.removed.empty()); @@ -1660,7 +1710,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil((valKeys.size() - 6) * 0.8f)); @@ -1673,7 +1723,9 @@ private: { auto const& val = valKeys[i]; if (i >= 2 && i < maxKeys - 4) + { BEAST_EXPECT(trustedKeys->trusted(val.masterPublic)); + } else { BEAST_EXPECT(!trustedKeys->trusted(val.masterPublic)); @@ -1691,7 +1743,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::numeric_limits::max()); @@ -1701,9 +1753,13 @@ private: { auto const& val = valKeys[i]; if (i < maxKeys - 4) + { BEAST_EXPECT(trustedKeys->listed(val.masterPublic)); + } else + { BEAST_EXPECT(!trustedKeys->listed(val.masterPublic)); + } BEAST_EXPECT(!trustedKeys->trusted(val.masterPublic)); if (i >= 2 && i < maxKeys - 4) @@ -1743,7 +1799,7 @@ private: BEAST_EXPECT(trustedKeys->expires() == std::nullopt); // Config listed keys have maximum expiry - PublicKey localCfgListed = randomNode(); + PublicKey const localCfgListed = randomNode(); trustedKeys->load({}, {toStr(localCfgListed)}, {}); BEAST_EXPECT( trustedKeys->expires() && @@ -1757,7 +1813,7 @@ private: auto trustedKeys = std::make_unique( manifests, manifests, - env.app().timeKeeper(), + env.app().getTimeKeeper(), app.config().legacy("database_path"), env.journal); @@ -1787,8 +1843,8 @@ private: pubSigningKeys.second, 1)); - std::vector cfgPublishers({strHex(publisherPublic)}); - std::vector emptyCfgKeys; + std::vector const cfgPublishers({strHex(publisherPublic)}); + std::vector const emptyCfgKeys; BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublishers)); @@ -1820,7 +1876,7 @@ private: // Configure two publishers and prepare 2 lists PreparedList prep1 = addPublishedList(); env.timeKeeper().set(env.timeKeeper().now() + 200s); - PreparedList prep2 = addPublishedList(); + PreparedList const prep2 = addPublishedList(); // Initially, no list has been published, so no known expiration BEAST_EXPECT(trustedKeys->expires() == std::nullopt); @@ -1855,7 +1911,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT( trustedKeys->expires() && @@ -1872,7 +1928,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT( trustedKeys->expires() && @@ -1901,7 +1957,7 @@ private: env.journal, minimumQuorum); - std::vector cfgPublishers; + std::vector const cfgPublishers; std::vector cfgKeys; hash_set activeValidators; cfgKeys.reserve(vlSize); @@ -1917,7 +1973,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); if (minimumQuorum == trustedKeys->quorum() || trustedKeys->quorum() == std::ceil(cfgKeys.size() * 0.8f)) @@ -1941,10 +1997,10 @@ private: */ { - hash_set activeValidators; + hash_set const activeValidators; //== Combinations == - std::array unlSizes = {34, 35, 39, 60}; - std::array nUnlPercent = {0, 20, 30, 50}; + std::array const unlSizes = {34, 35, 39, 60}; + std::array const nUnlPercent = {0, 20, 30, 50}; for (auto us : unlSizes) { for (auto np : nUnlPercent) @@ -1953,7 +2009,7 @@ private: BEAST_EXPECT(validators); if (validators) { - std::uint32_t nUnlSize = us * np / 100; + std::uint32_t const nUnlSize = us * np / 100; auto unl = validators->getTrustedMasterKeys(); hash_set nUnl; auto it = unl.begin(); @@ -1967,7 +2023,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT( validators->quorum() == @@ -2004,14 +2060,14 @@ private: { for (auto& n : nUnl_temp) { - if (nUnl.find(n) == nUnl.end()) + if (!nUnl.contains(n)) return false; } validators->updateTrusted( activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); return validators->quorum() == quorum; } @@ -2028,12 +2084,12 @@ private: // 18 auto nUnl = validators->getNegativeUNL(); BEAST_EXPECT(nUnl.size() == 12); - std::size_t ss = 33; + std::size_t const ss = 33; std::vector data(ss, 0); data[0] = 0xED; for (int i = 0; i < 6; ++i) { - Slice s(data.data(), ss); + Slice const s(data.data(), ss); data[1]++; nUnl.emplace(s); } @@ -2042,7 +2098,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(validators->quorum() == 39); } @@ -2069,7 +2125,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(validators->quorum() == 30); hash_set nUnl; @@ -2084,7 +2140,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(validators->quorum() == 30); } @@ -2113,7 +2169,7 @@ private: BEAST_EXPECT(global != sha512Half(signature, blobVector, version)); { - std::map blobMap{{99, blobVector[0]}}; + std::map const blobMap{{99, blobVector[0]}}; BEAST_EXPECT(global == sha512Half(manifest, blobMap, version)); BEAST_EXPECT(global != sha512Half(blob, blobMap, version)); } @@ -2322,7 +2378,7 @@ private: 1, 8, maxSequence, version, manifest, blobInfos, messages), 0, 0); - BEAST_EXPECT(messages.size() == 0); + BEAST_EXPECT(messages.empty()); // Don't repeat the work if messages is populated, even though the // peerSequence provided indicates it should. Note that this @@ -2355,12 +2411,14 @@ private: if (BEAST_EXPECT(msg)) { BEAST_EXPECT(msg->version() == 1); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(msg->manifest() == *expected.manifest); BEAST_EXPECT(msg->blob() == expected.blob); BEAST_EXPECT(msg->signature() == expected.signature); } BEAST_EXPECT( messageWithHash.hash == + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) sha512Half(*expected.manifest, expected.blob, expected.signature, 1)); } @@ -2374,7 +2432,7 @@ private: 2, maxSequence * 2, maxSequence, version, manifest, blobInfos, messages), 0, 0); - BEAST_EXPECT(messages.size() == 0); + BEAST_EXPECT(messages.empty()); // Don't repeat the work if messages is populated, even though the // peerSequence provided indicates it should. Note that this @@ -2473,7 +2531,7 @@ private: PublicKey pubKey; std::pair signingKeys; std::string manifest; - NetClock::time_point expiry = {}; + NetClock::time_point expiry = {}; // NOLINT(readability-redundant-member-init) }; // Create ValidatorList with a set of countTotal publishers, of which @@ -2510,7 +2568,12 @@ private: pubSigningKeys.second, i < countRevoked ? revoked : 1)); publishers.push_back( - Publisher{i < countRevoked, publisherPublic, pubSigningKeys, manifest}); + Publisher{ + .revoked = i < countRevoked, + .pubKey = publisherPublic, + .signingKeys = pubSigningKeys, + .manifest = manifest, + }); } std::vector const emptyCfgKeys; @@ -2570,7 +2633,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -2591,7 +2654,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 1); @@ -2629,7 +2692,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -2649,10 +2712,10 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); - BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 0); + BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().empty()); hash_set removed; for (auto const& val : valKeys) @@ -2694,7 +2757,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -2714,7 +2777,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 1); @@ -2763,7 +2826,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -2784,7 +2847,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 1); @@ -2830,7 +2893,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -2850,7 +2913,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 1); @@ -2897,7 +2960,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -2917,10 +2980,10 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); - BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 0); + BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().empty()); hash_set removed; for (auto const& val : valKeys) @@ -2962,7 +3025,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -2983,7 +3046,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3021,7 +3084,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3042,7 +3105,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3080,7 +3143,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3100,7 +3163,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3136,7 +3199,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3156,7 +3219,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3201,7 +3264,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3222,7 +3285,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 1); @@ -3268,7 +3331,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3288,7 +3351,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 1); @@ -3335,7 +3398,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3355,10 +3418,10 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); - BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 0); + BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().empty()); hash_set removed; for (auto const& val : valKeys) @@ -3394,7 +3457,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3415,7 +3478,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 1); @@ -3455,7 +3518,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3476,7 +3539,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 1); @@ -3517,7 +3580,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3537,10 +3600,10 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); - BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 0); + BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().empty()); hash_set removed; for (auto const& val : valKeys) @@ -3578,7 +3641,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3599,7 +3662,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 1); @@ -3637,11 +3700,11 @@ private: for (auto const& p : publishers) BEAST_EXPECT(trustedKeys->trustedPublisher(p.pubKey)); - TrustChanges changes = trustedKeys->updateTrusted( + TrustChanges const changes = trustedKeys->updateTrusted( activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3675,11 +3738,11 @@ private: for (auto const& p : publishers) BEAST_EXPECT(trustedKeys->trustedPublisher(p.pubKey)); - TrustChanges changes = trustedKeys->updateTrusted( + TrustChanges const changes = trustedKeys->updateTrusted( activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3725,7 +3788,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3746,7 +3809,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 1); @@ -3794,7 +3857,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == std::ceil(keysTotal * 0.8f)); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == keysTotal); @@ -3814,7 +3877,7 @@ private: activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 1); diff --git a/src/test/app/ValidatorSite_test.cpp b/src/test/app/ValidatorSite_test.cpp index 5fc290eda5..9f9b0cbdf7 100644 --- a/src/test/app/ValidatorSite_test.cpp +++ b/src/test/app/ValidatorSite_test.cpp @@ -53,11 +53,11 @@ private: auto trustedSites = std::make_unique(env.app(), env.journal); // load should accept empty sites list - std::vector emptyCfgSites; + std::vector const emptyCfgSites; BEAST_EXPECT(trustedSites->load(emptyCfgSites)); // load should accept valid validator site uris - std::vector cfgSites( + std::vector const cfgSites( {"http://ripple.com/", "http://ripple.com/validators", "http://ripple.com:8080/validators", @@ -65,7 +65,7 @@ private: "http://207.261.33.37:8080/validators", "https://ripple.com/validators", "https://ripple.com:443/validators", - "file:///etc/opt/ripple/validators.txt", + "file:///etc/opt/xrpld/validators.txt", "file:///C:/Lib/validators.txt" #if !_MSC_VER , @@ -139,13 +139,13 @@ private: p->legacy("database_path", good.subdir().string()); return p; }()); - auto& trustedKeys = env.app().validators(); + auto& trustedKeys = env.app().getValidators(); env.timeKeeper().set(env.timeKeeper().now() + 30s); test::StreamSink sink; beast::Journal journal{sink}; - std::vector emptyCfgKeys; + std::vector const emptyCfgKeys; struct publisher { publisher(FetchListConfig const& c) : cfg{c} @@ -155,7 +155,7 @@ private: std::vector list; std::string uri; FetchListConfig const& cfg; - bool isRetry; + bool isRetry{}; }; std::vector servers; @@ -181,7 +181,7 @@ private: {{effective2, expires2}}, cfg.ssl, cfg.serverVersion); - std::string pubHex = strHex(item.server->publisherPublic()); + std::string const pubHex = strHex(item.server->publisherPublic()); cfgPublishers.push_back(pubHex); if (item.cfg.failFetch) @@ -225,8 +225,10 @@ private: Json::Value myStatus; for (auto const& vs : jv[jss::validator_sites]) + { if (vs[jss::uri].asString().find(u.uri) != std::string::npos) myStatus = vs; + } BEAST_EXPECTS( myStatus[jss::last_refresh_message].asString().empty() != u.cfg.failFetch, to_string(myStatus) + "\n" + sink.messages().str()); @@ -238,7 +240,7 @@ private: sink.messages().str()); } - if (u.cfg.expectedRefreshMin) + if (u.cfg.expectedRefreshMin != 0) { BEAST_EXPECTS( myStatus[jss::refresh_interval_min].asInt() == u.cfg.expectedRefreshMin, @@ -296,6 +298,7 @@ private: auto sites = std::make_unique(env.app(), journal); std::vector uris; + uris.reserve(servers.size()); for (auto const& u : servers) uris.push_back(u.uri); sites->load(uris); @@ -307,8 +310,10 @@ private: auto const jv = sites->getJson(); Json::Value myStatus; for (auto const& vs : jv[jss::validator_sites]) + { if (vs[jss::uri].asString().find(u.uri) != std::string::npos) myStatus = vs; + } BEAST_EXPECTS( myStatus[jss::last_refresh_message].asString().empty() != u.shouldFail, to_string(myStatus)); @@ -332,11 +337,12 @@ private: }; { // Create a file with a real validator list - detail::FileDirGuard good(*this, "test_val", "vl.txt", detail::realValidatorContents()); + detail::FileDirGuard const good( + *this, "test_val", "vl.txt", detail::realValidatorContents()); // Create a file with arbitrary content - detail::FileDirGuard hello(*this, "test_val", "helloworld.txt", "Hello, world!"); + detail::FileDirGuard const hello(*this, "test_val", "helloworld.txt", "Hello, world!"); // Create a file with malformed Json - detail::FileDirGuard json( + detail::FileDirGuard const json( *this, "test_val", "json.txt", R"json({ "version": 2, "extra" : "value" })json"); auto const goodPath = fullPath(good); auto const helloPath = fullPath(hello); @@ -357,7 +363,7 @@ public: { testConfigLoad(); - detail::DirGuard good(*this, "test_fetch"); + detail::DirGuard const good(*this, "test_fetch"); for (auto ssl : {true, false}) { // fetch single site diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index 7ae9faf18f..fd4cbd5334 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -2,16 +2,17 @@ #include #include #include +#include #include -#include #include #include #include #include #include +#include #include -#include +#include #include #include #include @@ -67,23 +68,31 @@ class Vault_test : public beast::unit_test::suite auto const vault = env.le(keylet); BEAST_EXPECT(vault != nullptr); if (!asset.integral()) + { BEAST_EXPECT(vault->at(sfScale) == 6); + } else + { BEAST_EXPECT(vault->at(sfScale) == 0); + } auto const shares = env.le(keylet::mptIssuance(vault->at(sfShareMPTID))); BEAST_EXPECT(shares != nullptr); if (!asset.integral()) + { BEAST_EXPECT(shares->at(sfAssetScale) == 6); + } else + { BEAST_EXPECT(shares->at(sfAssetScale) == 0); + } return {MPTIssue(vault->at(sfShareMPTID)), Account("vault", vault->at(sfAccount))}; }(); auto const shares = share.raw().get(); env.memoize(vaultAccount); // Several 3rd party accounts which cannot receive funds - Account alice{"alice"}; - Account erin{"erin"}; // not authorized by issuer + Account const alice{"alice"}; + Account const erin{"erin"}; // not authorized by issuer env.fund(XRP(1000), alice, erin); env(fset(alice, asfDepositAuth)); env.close(); @@ -506,14 +515,14 @@ class Vault_test : public beast::unit_test::suite env.require(flags(issuer, asfAllowTrustLineClawback)); env.require(flags(issuer, asfRequireAuth)); - PrettyAsset asset = setup(env); + PrettyAsset const asset = setup(env); testSequence(prefix, env, vault, asset); }; testCases("XRP", [&](Env& env) -> PrettyAsset { return {xrpIssue(), 1'000'000}; }); testCases("IOU", [&](Env& env) -> Asset { - PrettyAsset asset = issuer["IOU"]; + PrettyAsset const asset = issuer["IOU"]; env(trust(owner, asset(1000))); env(trust(depositor, asset(1000))); env(trust(charlie, asset(1000))); @@ -530,7 +539,7 @@ class Vault_test : public beast::unit_test::suite testCases("MPT", [&](Env& env) -> Asset { MPTTester mptt{env, issuer, mptInitNoFund}; mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); - PrettyAsset asset = mptt.issuanceID(); + PrettyAsset const asset = mptt.issuanceID(); mptt.authorize({.account = depositor}); mptt.authorize({.account = charlie}); mptt.authorize({.account = dave}); @@ -559,8 +568,8 @@ class Vault_test : public beast::unit_test::suite Vault& vault)> test, CaseArgs args = {}) { Env env{*this, args.features}; - Account issuer{"issuer"}; - Account owner{"owner"}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; Vault vault{env}; env.fund(XRP(1000), issuer, owner); env.close(); @@ -569,7 +578,7 @@ class Vault_test : public beast::unit_test::suite env(fset(issuer, asfRequireAuth)); env.close(); - PrettyAsset asset = issuer["IOU"]; + PrettyAsset const asset = issuer["IOU"]; env(trust(owner, asset(1000))); env(trust(issuer, asset(0), owner, tfSetfAuth)); env(pay(issuer, owner, asset(1000))); @@ -1073,13 +1082,13 @@ class Vault_test : public beast::unit_test::suite Asset const& asset, Vault& vault)> test) { Env env{*this, testable_amendments() | featureSingleAssetVault}; - Account issuer{"issuer"}; - Account owner{"owner"}; - Account depositor{"depositor"}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + Account const depositor{"depositor"}; env.fund(XRP(1000), issuer, owner, depositor); env.close(); Vault vault{env}; - Asset asset = xrpIssue(); + Asset const asset = xrpIssue(); test(env, issuer, owner, depositor, asset, vault); }; @@ -1238,13 +1247,13 @@ class Vault_test : public beast::unit_test::suite testcase("IOU fail because MPT is disabled"); Env env{ *this, (testable_amendments() - featureMPTokensV1) | featureSingleAssetVault}; - Account issuer{"issuer"}; - Account owner{"owner"}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; env.fund(XRP(1000), issuer, owner); env.close(); - Vault vault{env}; - Asset asset = issuer["IOU"].asset(); + Vault const vault{env}; + Asset const asset = issuer["IOU"].asset(); auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); env(tx, ter(temDISABLED)); @@ -1254,15 +1263,15 @@ class Vault_test : public beast::unit_test::suite { testcase("IOU fail create frozen"); Env env{*this, testable_amendments() | featureSingleAssetVault}; - Account issuer{"issuer"}; - Account owner{"owner"}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; env.fund(XRP(1000), issuer, owner); env.close(); env(fset(issuer, asfGlobalFreeze)); env.close(); - Vault vault{env}; - Asset asset = issuer["IOU"].asset(); + Vault const vault{env}; + Asset const asset = issuer["IOU"].asset(); auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); env(tx, ter(tecFROZEN)); @@ -1272,15 +1281,15 @@ class Vault_test : public beast::unit_test::suite { testcase("IOU fail create no ripling"); Env env{*this, testable_amendments() | featureSingleAssetVault}; - Account issuer{"issuer"}; - Account owner{"owner"}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; env.fund(XRP(1000), issuer, owner); env.close(); env(fclear(issuer, asfDefaultRipple)); env.close(); - Vault vault{env}; - Asset asset = issuer["IOU"].asset(); + Vault const vault{env}; + Asset const asset = issuer["IOU"].asset(); auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); env(tx, ter(terNO_RIPPLE)); env.close(); @@ -1289,13 +1298,13 @@ class Vault_test : public beast::unit_test::suite { testcase("IOU no issuer"); Env env{*this, testable_amendments() | featureSingleAssetVault}; - Account issuer{"issuer"}; - Account owner{"owner"}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; env.fund(XRP(1000), owner); env.close(); - Vault vault{env}; - Asset asset = issuer["IOU"].asset(); + Vault const vault{env}; + Asset const asset = issuer["IOU"].asset(); { auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); env(tx, ter(terNO_ACCOUNT)); @@ -1321,7 +1330,7 @@ class Vault_test : public beast::unit_test::suite return defXRP; return a + XRP(1000); } - auto const defIOU = STAmount{a.issue(), 30000}; + auto defIOU = STAmount{a.issue(), 30000}; if (a <= defIOU) return defIOU; return a + STAmount{a.issue(), 1000}; @@ -1331,18 +1340,24 @@ class Vault_test : public beast::unit_test::suite BEAST_EXPECT(asset1 <= toFund1 && asset2 <= toFund2); if (!asset1.native() && !asset2.native()) + { fund(env, gw, {alice, carol}, {toFund1, toFund2}, Fund::All); + } else if (asset1.native()) + { fund(env, gw, {alice, carol}, toFund1, {toFund2}, Fund::All); + } else if (asset2.native()) + { fund(env, gw, {alice, carol}, toFund2, {toFund1}, Fund::All); + } - AMM ammAlice(env, alice, asset1, asset2, CreateArg{.log = false, .tfee = 0}); + AMM const ammAlice(env, alice, asset1, asset2, CreateArg{.log = false, .tfee = 0}); Account const owner{"owner"}; env.fund(XRP(1000000), owner); - Vault vault{env}; + Vault const vault{env}; auto [tx, k] = vault.create({.owner = owner, .asset = ammAlice.lptIssue()}); env(tx, ter{tecWRONG_ASSET}); env.close(); @@ -1363,16 +1378,16 @@ class Vault_test : public beast::unit_test::suite Asset const& asset, Vault& vault)> test) { Env env{*this, testable_amendments() | featureSingleAssetVault}; - Account issuer{"issuer"}; - Account owner{"owner"}; - Account depositor{"depositor"}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + Account const depositor{"depositor"}; env.fund(XRP(1000), issuer, owner, depositor); env.close(); Vault vault{env}; MPTTester mptt{env, issuer, mptInitNoFund}; // Locked because that is the default flag. mptt.create(); - Asset asset = mptt.issuanceID(); + Asset const asset = mptt.issuanceID(); test(env, issuer, owner, depositor, asset, vault); }; @@ -1422,14 +1437,14 @@ class Vault_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this, testable_amendments() | featureSingleAssetVault}; - Account issuer{"issuer"}; - Account owner{"owner"}; - Account depositor{"depositor"}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + Account const depositor{"depositor"}; env.fund(XRP(1000), issuer, owner, depositor); env.close(); - Vault vault{env}; - PrettyAsset asset = issuer["IOU"]; + Vault const vault{env}; + PrettyAsset const asset = issuer["IOU"]; env.trust(asset(1000), owner); env(pay(issuer, owner, asset(100))); env.trust(asset(1000), depositor); @@ -1466,7 +1481,7 @@ class Vault_test : public beast::unit_test::suite }(); auto const MptID = makeMptID(1, vaultAccount); - Asset shares = MptID; + Asset const shares = MptID; { testcase("nontransferable shares cannot be moved"); @@ -1549,7 +1564,7 @@ class Vault_test : public beast::unit_test::suite (args.enableClawback ? tfMPTCanClawback : none) | (args.requireAuth ? tfMPTRequireAuth : none), .mutableFlags = tmfMPTCanMutateCanTransfer}); - PrettyAsset asset = mptt.issuanceID(); + PrettyAsset const asset = mptt.issuanceID(); mptt.authorize({.account = owner}); mptt.authorize({.account = depositor}); if (args.requireAuth) @@ -1640,10 +1655,10 @@ class Vault_test : public beast::unit_test::suite // accounts for the issued shares. auto v = env.le(keylet); BEAST_EXPECT(v); - MPTID share = (*v)[sfShareMPTID]; + MPTID const share = (*v)[sfShareMPTID]; auto issuance = env.le(keylet::mptIssuance(share)); BEAST_EXPECT(issuance); - Number outstandingShares = issuance->at(sfOutstandingAmount); + Number const outstandingShares = issuance->at(sfOutstandingAmount); BEAST_EXPECT(outstandingShares == 100); mptt.set({.account = issuer, .flags = tfMPTLock}); @@ -1747,7 +1762,7 @@ class Vault_test : public beast::unit_test::suite { // Set destination to 3rd party without MPToken - Account charlie{"charlie"}; + Account const charlie{"charlie"}; env.fund(XRP(1000), charlie); env.close(); @@ -1824,7 +1839,7 @@ class Vault_test : public beast::unit_test::suite {.requireAuth = false}); auto const [acctReserve, incReserve] = [this]() -> std::pair { - Env env{*this, testable_amendments()}; + Env const env{*this, testable_amendments()}; return { env.current()->fees().accountReserve(0).drops() / DROPS_PER_XRP.drops(), env.current()->fees().increment.drops() / DROPS_PER_XRP.drops()}; @@ -1884,7 +1899,7 @@ class Vault_test : public beast::unit_test::suite env.close(); } }, - {.requireAuth = false, .initialXRP = acctReserve + incReserve * 4 + 1}); + {.requireAuth = false, .initialXRP = acctReserve + (incReserve * 4) + 1}); testCase([this]( Env& env, @@ -1961,7 +1976,7 @@ class Vault_test : public beast::unit_test::suite auto const vault = env.le(keylet); return vault->at(sfShareMPTID); }(keylet); - PrettyAsset shares = MPTIssue(issuanceId); + PrettyAsset const shares = MPTIssue(issuanceId); { // owner has MPToken for shares they did not explicitly create @@ -2163,14 +2178,14 @@ class Vault_test : public beast::unit_test::suite Account issuer{"issuer"}; env.fund(XRP(1000000), owner, issuer); env.close(); - Vault vault{env}; + Vault const vault{env}; MPTTester mptt{env, issuer, mptInitNoFund}; mptt.create( {.flags = tfMPTCanTransfer | tfMPTCanLock | lsfMPTCanClawback | tfMPTRequireAuth}); mptt.authorize({.account = owner}); mptt.authorize({.account = issuer, .holder = owner}); - PrettyAsset asset = mptt.issuanceID(); + PrettyAsset const asset = mptt.issuanceID(); env(pay(issuer, owner, asset(100))); auto [tx1, k1] = vault.create({.owner = owner, .asset = asset}); env(tx1); @@ -2277,7 +2292,9 @@ class Vault_test : public beast::unit_test::suite env(fset(issuer, asfDefaultRipple)); } else + { env.trust(asset(1000), charlie); + } env.close(); env(rate(issuer, args.transferRate)); env.close(); @@ -2623,28 +2640,28 @@ class Vault_test : public beast::unit_test::suite // Turn on noripple on the pseudo account's trust line. // Charlie's is already set. - env(trust(issuer, vaultAccount(keylet)["IOU"], tfSetNoRipple), THISLINE); + env(trust(issuer, vaultAccount(keylet)["IOU"], tfSetNoRipple)); { // Charlie cannot deposit auto tx = vault.deposit( {.depositor = charlie, .id = keylet.key, .amount = asset(100)}); - env(tx, ter{terNO_RIPPLE}, THISLINE); + env(tx, ter{terNO_RIPPLE}); env.close(); } { - PrettyAsset shares = issuanceId(keylet); + PrettyAsset const shares = issuanceId(keylet); auto tx1 = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}); - env(tx1, THISLINE); + env(tx1); env.close(); // Charlie cannot receive funds auto tx2 = vault.withdraw( {.depositor = owner, .id = keylet.key, .amount = shares(100)}); tx2[sfDestination] = charlie.human(); - env(tx2, ter{terNO_RIPPLE}, THISLINE); + env(tx2, ter{terNO_RIPPLE}); env.close(); { @@ -2657,7 +2674,7 @@ class Vault_test : public beast::unit_test::suite env(tx); env.close(); } - env(pay(owner, charlie, shares(100)), THISLINE); + env(pay(owner, charlie, shares(100))); env.close(); // Charlie cannot withdraw @@ -2666,16 +2683,16 @@ class Vault_test : public beast::unit_test::suite env(tx3, ter{terNO_RIPPLE}); env.close(); - env(pay(charlie, owner, shares(100)), THISLINE); + env(pay(charlie, owner, shares(100))); env.close(); } tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(100)}); - env(tx, THISLINE); + env(tx); env.close(); // Delete vault with zero balance - env(vault.del({.owner = owner, .id = keylet.key}), THISLINE); + env(vault.del({.owner = owner, .id = keylet.key})); }, {.charlieRipple = false}); @@ -2729,7 +2746,7 @@ class Vault_test : public beast::unit_test::suite env(vault.withdraw( {.depositor = owner, .id = keylet.key, - .amount = asset(Number(1000 + 37 * 5, -1))})); + .amount = asset(Number(1000 + (37 * 5), -1))})); { BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value()); @@ -2745,7 +2762,7 @@ class Vault_test : public beast::unit_test::suite {.initialIOU = Number(11875, -2)}); auto const [acctReserve, incReserve] = [this]() -> std::pair { - Env env{*this, testable_amendments()}; + Env const env{*this, testable_amendments()}; return { env.current()->fees().accountReserve(0).drops() / DROPS_PER_XRP.drops(), env.current()->fees().increment.drops() / DROPS_PER_XRP.drops()}; @@ -2792,7 +2809,7 @@ class Vault_test : public beast::unit_test::suite env(tx); env.close(); }, - CaseArgs{.initialXRP = acctReserve + incReserve * 4 + 1}); + CaseArgs{.initialXRP = acctReserve + (incReserve * 4) + 1}); testCase( [&, this]( @@ -2827,7 +2844,7 @@ class Vault_test : public beast::unit_test::suite env(tx); env.close(); }, - CaseArgs{.initialXRP = acctReserve + incReserve * 4 + 1}); + CaseArgs{.initialXRP = acctReserve + (incReserve * 4) + 1}); testCase([&, this]( Env& env, @@ -2934,22 +2951,22 @@ class Vault_test : public beast::unit_test::suite testcase("private vault"); Env env{*this, testable_amendments() | featureSingleAssetVault}; - Account issuer{"issuer"}; - Account owner{"owner"}; - Account depositor{"depositor"}; - Account charlie{"charlie"}; - Account pdOwner{"pdOwner"}; - Account credIssuer1{"credIssuer1"}; - Account credIssuer2{"credIssuer2"}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + Account const depositor{"depositor"}; + Account const charlie{"charlie"}; + Account const pdOwner{"pdOwner"}; + Account const credIssuer1{"credIssuer1"}; + Account const credIssuer2{"credIssuer2"}; std::string const credType = "credential"; - Vault vault{env}; + Vault const vault{env}; env.fund(XRP(1000), issuer, owner, depositor, charlie, pdOwner, credIssuer1, credIssuer2); env.close(); env(fset(issuer, asfAllowTrustLineClawback)); env.close(); env.require(flags(issuer, asfAllowTrustLineClawback)); - PrettyAsset asset = issuer["IOU"]; + PrettyAsset const asset = issuer["IOU"]; env.trust(asset(1000), owner); env(pay(issuer, owner, asset(500))); env.trust(asset(1000), depositor); @@ -3182,15 +3199,15 @@ class Vault_test : public beast::unit_test::suite testcase("private XRP vault"); Env env{*this, testable_amendments() | featureSingleAssetVault}; - Account owner{"owner"}; - Account depositor{"depositor"}; - Account alice{"charlie"}; + Account const owner{"owner"}; + Account const depositor{"depositor"}; + Account const alice{"charlie"}; std::string const credType = "credential"; - Vault vault{env}; + Vault const vault{env}; env.fund(XRP(100000), owner, depositor, alice); env.close(); - PrettyAsset asset = xrpIssue(); + PrettyAsset const asset = xrpIssue(); auto [tx, keylet] = vault.create({.owner = owner, .asset = asset, .flags = tfVaultPrivate}); env(tx); env.close(); @@ -3203,7 +3220,7 @@ class Vault_test : public beast::unit_test::suite }(); BEAST_EXPECT(env.le(keylet::account(vaultAccount))); BEAST_EXPECT(env.le(keylet::mptIssuance(issuanceId))); - PrettyAsset shares{issuanceId}; + PrettyAsset const shares{issuanceId}; { testcase("private XRP vault owner can deposit"); @@ -3280,7 +3297,7 @@ class Vault_test : public beast::unit_test::suite testcase("fail pseudo-account allocation"); Env env{*this, testable_amendments() | featureSingleAssetVault}; Account const owner{"owner"}; - Vault vault{env}; + Vault const vault{env}; env.fund(XRP(1000), owner); auto const keylet = keylet::vault(owner.id(), env.seq(owner)); @@ -3346,27 +3363,28 @@ class Vault_test : public beast::unit_test::suite auto const vault = env.le(keylet); return {Account("vault", vault->at(sfAccount)), vault->at(sfShareMPTID)}; }(keylet); - MPTIssue shares(issuanceId); + MPTIssue const shares(issuanceId); env.memoize(vaultAccount); - auto const peek = [=, &env, this](std::function fn) -> bool { - return env.app().openLedger().modify([&](OpenView& view, beast::Journal j) -> bool { - Sandbox sb(&view, tapNONE); - auto vault = sb.peek(keylet::vault(keylet.key)); - if (!BEAST_EXPECT(vault != nullptr)) + auto const peek = [keylet, &env, this](std::function fn) -> bool { + return env.app().getOpenLedger().modify( + [&](OpenView& view, beast::Journal j) -> bool { + Sandbox sb(&view, tapNONE); + auto vault = sb.peek(keylet::vault(keylet.key)); + if (!BEAST_EXPECT(vault)) + return false; + auto shares = sb.peek(keylet::mptIssuance(vault->at(sfShareMPTID))); + if (!BEAST_EXPECT(shares)) + return false; + if (fn(*vault, *shares)) + { + sb.update(vault); + sb.update(shares); + sb.apply(view); + return true; + } return false; - auto shares = sb.peek(keylet::mptIssuance(vault->at(sfShareMPTID))); - if (!BEAST_EXPECT(shares != nullptr)) - return false; - if (fn(*vault, *shares)) - { - sb.update(vault); - sb.update(shares); - sb.apply(view); - return true; - } - return false; - }); + }); }; test( @@ -4085,6 +4103,66 @@ class Vault_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0); } }); + + // Non-1:1 ratio (scale=1, 10:1 shares:assets) with an outstanding loan. + // Deposit 100 IOU → 1000 shares. Borrow 40 → assetsAvailable=60. + // Clawback 80 IOU → clamped to 60, then share math uses truncation. + testCase(1, [&, this](Env& env, Data d) { + using namespace loanBroker; + using namespace loan; + + testcase("Scale clawback clamped with outstanding loan"); + + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(100, 0))}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000)); + + // Create a loan broker backed by this vault + auto const brokerKeylet = keylet::loanbroker(d.owner.id(), env.seq(d.owner)); + env(set(d.owner, d.keylet.key)); + env.close(); + + // Borrow 40: assetsAvailable=60, assetsTotal=100 + env(set(d.depositor, brokerKeylet.key, STAmount(d.asset, Number(40, 0))), + loan::interestRate(TenthBips32(0)), + gracePeriod(60), + paymentInterval(120), + paymentTotal(10), + sig(sfCounterpartySignature, d.owner), + fee(env.current()->fees().base * 2), + ter(tesSUCCESS)); + env.close(); + + { + auto const sle = env.le(d.keylet); + BEAST_EXPECT(sle->at(sfAssetsAvailable) == STAmount(d.asset, Number(60, 0))); + BEAST_EXPECT(sle->at(sfAssetsTotal) == STAmount(d.asset, Number(100, 0))); + } + + // Request 80 IOU clawback — clamped to assetsAvailable (60) + // With scale=1 (10:1), 60 assets = 600 shares destroyed + tx = d.vault.clawback( + {.issuer = d.issuer, + .id = d.keylet.key, + .holder = d.depositor, + .amount = STAmount(d.asset, Number(80, 0))}); + env(tx, ter(tesSUCCESS)); + env.close(); + + { + auto const sle = env.le(d.keylet); + BEAST_EXPECT(sle != nullptr); + BEAST_EXPECT(sle->at(sfAssetsAvailable) == STAmount(d.asset, Number(0, 0))); + BEAST_EXPECT(sle->at(sfAssetsTotal) == STAmount(d.asset, Number(40, 0))); + + // 600 of 1000 shares destroyed, 400 remain + BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(400)); + } + }); } void @@ -4096,11 +4174,11 @@ class Vault_test : public beast::unit_test::suite Env env{*this, testable_amendments() | featureSingleAssetVault}; Account const owner{"owner"}; Account const issuer{"issuer"}; - Vault vault{env}; + Vault const vault{env}; env.fund(XRP(1000), issuer, owner); env.close(); - PrettyAsset asset = issuer["IOU"]; + PrettyAsset const asset = issuer["IOU"]; env.trust(asset(1000), owner); env(pay(issuer, owner, asset(200))); env.close(); @@ -4500,131 +4578,6 @@ class Vault_test : public beast::unit_test::suite } } - void - testDelegate() - { - using namespace test::jtx; - - Env env(*this, testable_amendments()); - Account alice{"alice"}; - Account bob{"bob"}; - Account carol{"carol"}; - - struct CaseArgs - { - PrettyAsset asset = xrpIssue(); - }; - - auto const xrpBalance = [this]( - Env const& env, Account const& account) -> std::optional { - auto sle = env.le(keylet::account(account.id())); - if (BEAST_EXPECT(sle != nullptr)) - return sle->getFieldAmount(sfBalance).xrp().drops(); - return std::nullopt; - }; - - auto testCase = [&, this](auto test, CaseArgs args = {}) { - Env env{*this, testable_amendments() | featureSingleAssetVault}; - - Vault vault{env}; - - // use different initial amount to distinguish the source balance - env.fund(XRP(10000), alice); - env.fund(XRP(20000), bob); - env.fund(XRP(30000), carol); - env.close(); - - env(delegate::set( - carol, - alice, - {"Payment", - "VaultCreate", - "VaultSet", - "VaultDelete", - "VaultDeposit", - "VaultWithdraw", - "VaultClawback"})); - - test(env, vault, args.asset); - }; - - testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) { - testcase("delegated vault creation"); - auto startBalance = xrpBalance(env, carol); - if (!BEAST_EXPECT(startBalance.has_value())) - return; - - auto [tx, keylet] = vault.create({.owner = carol, .asset = asset}); - env(tx, delegate::as(alice)); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance); - }); - - testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) { - testcase("delegated deposit and withdrawal"); - auto [tx, keylet] = vault.create({.owner = carol, .asset = asset}); - env(tx); - env.close(); - - auto const amount = 1513; - auto const baseFee = env.current()->fees().base; - - auto startBalance = xrpBalance(env, carol); - if (!BEAST_EXPECT(startBalance.has_value())) - return; - - tx = vault.deposit({.depositor = carol, .id = keylet.key, .amount = asset(amount)}); - env(tx, delegate::as(alice)); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount); - - tx = - vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(amount - 1)}); - env(tx, delegate::as(alice)); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - 1); - - tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(1)}); - env(tx); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee); - }); - - testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) { - testcase("delegated withdrawal same as base fee and deletion"); - auto [tx, keylet] = vault.create({.owner = carol, .asset = asset}); - env(tx); - env.close(); - - auto const amount = 25537; - auto const baseFee = env.current()->fees().base; - - auto startBalance = xrpBalance(env, carol); - if (!BEAST_EXPECT(startBalance.has_value())) - return; - - tx = vault.deposit({.depositor = carol, .id = keylet.key, .amount = asset(amount)}); - env(tx); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount - baseFee); - - tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(baseFee)}); - env(tx, delegate::as(alice)); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount); - - tx = vault.withdraw( - {.depositor = carol, .id = keylet.key, .amount = asset(amount - baseFee)}); - env(tx, delegate::as(alice)); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee); - - tx = vault.del({.owner = carol, .id = keylet.key}); - env(tx, delegate::as(alice)); - env.close(); - }); - } - void testVaultClawbackBurnShares() { @@ -4653,21 +4606,20 @@ class Vault_test : public beast::unit_test::suite auto const setupVault = [&](PrettyAsset const& asset, Account const& owner, Account const& depositor) -> std::pair { - Vault vault{env}; + Vault const vault{env}; auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset}); - env(tx, ter(tesSUCCESS), THISLINE); + env(tx, ter(tesSUCCESS)); env.close(); auto const& vaultSle = env.le(vaultKeylet); BEAST_EXPECT(vaultSle != nullptr); - Asset share = vaultSle->at(sfShareMPTID); + Asset const share = vaultSle->at(sfShareMPTID); env(vault.deposit( {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); env.close(); auto const& [availablePreDefault, totalPreDefault] = vaultAssetBalance(vaultKeylet); @@ -4680,14 +4632,13 @@ class Vault_test : public beast::unit_test::suite .id = vaultKeylet.key, .holder = depositor, .amount = share(0).value()}), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); env.close(); auto const& sharesAvailable = vaultShareBalance(vaultKeylet); auto const& brokerKeylet = keylet::loanbroker(owner.id(), env.seq(owner)); - env(set(owner, vaultKeylet.key), THISLINE); + env(set(owner, vaultKeylet.key)); env.close(); auto const& loanKeylet = keylet::loan(brokerKeylet.key, 1); @@ -4700,8 +4651,7 @@ class Vault_test : public beast::unit_test::suite paymentTotal(10), sig(sfCounterpartySignature, owner), fee(env.current()->fees().base * 2), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); env.close(); // attempt to clawback shares while there assetsAvailable == 0 and @@ -4711,13 +4661,12 @@ class Vault_test : public beast::unit_test::suite .id = vaultKeylet.key, .holder = depositor, .amount = share(0).value()}), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); env.close(); env.close(std::chrono::seconds{120 + 60}); - env(manage(owner, loanKeylet.key, tfLoanDefault), ter(tesSUCCESS), THISLINE); + env(manage(owner, loanKeylet.key, tfLoanDefault), ter(tesSUCCESS)); auto const& [availablePostDefault, totalPostDefault] = vaultAssetBalance(vaultKeylet); @@ -4735,19 +4684,23 @@ class Vault_test : public beast::unit_test::suite { testcase("VaultClawback (share) - " + prefix + " owner asset clawback fails"); auto [vault, vaultKeylet] = setupVault(asset, owner, depositor); + // when asset is XRP or owner is not issuer clawback fail + // when owner is issuer precision loss occurs as vault is + // empty + auto const expectedTer = [&]() { + if (asset.native()) + return ter(temMALFORMED); + if (asset.raw().getIssuer() != owner.id()) + return ter(tecNO_PERMISSION); + return ter(tecPRECISION_LOSS); + }(); env(vault.clawback({ .issuer = owner, .id = vaultKeylet.key, .holder = depositor, .amount = asset(100).value(), }), - // when asset is XRP or owner is not issuer clawback fail - // when owner is issuer precision loss occurs as vault is - // empty - asset.native() ? ter(temMALFORMED) - : asset.raw().getIssuer() != owner.id() ? ter(tecNO_PERMISSION) - : ter(tecPRECISION_LOSS), - THISLINE); + expectedTer); env.close(); } @@ -4756,18 +4709,16 @@ class Vault_test : public beast::unit_test::suite "VaultClawback (share) - " + prefix + " owner incomplete share clawback fails"); auto [vault, vaultKeylet] = setupVault(asset, owner, depositor); auto const& vaultSle = env.le(vaultKeylet); - BEAST_EXPECT(vaultSle != nullptr); - if (!vaultSle) + if (!BEAST_EXPECT(vaultSle)) return; - Asset share = vaultSle->at(sfShareMPTID); + Asset const share = vaultSle->at(sfShareMPTID); env(vault.clawback({ .issuer = owner, .id = vaultKeylet.key, .holder = depositor, .amount = share(1).value(), }), - ter(tecLIMIT_EXCEEDED), - THISLINE); + ter(tecLIMIT_EXCEEDED)); env.close(); } @@ -4783,8 +4734,7 @@ class Vault_test : public beast::unit_test::suite }), // when owner is issuer implicit clawback fails asset.native() || asset.raw().getIssuer() != owner.id() ? ter(tesSUCCESS) - : ter(tecWRONG_ASSET), - THISLINE); + : ter(tecWRONG_ASSET)); env.close(); } @@ -4794,36 +4744,32 @@ class Vault_test : public beast::unit_test::suite " owner explicit complete share clawback succeeds"); auto [vault, vaultKeylet] = setupVault(asset, owner, depositor); auto const& vaultSle = env.le(vaultKeylet); - BEAST_EXPECT(vaultSle != nullptr); - if (!vaultSle) + if (!BEAST_EXPECT(vaultSle)) return; - Asset share = vaultSle->at(sfShareMPTID); + Asset const share = vaultSle->at(sfShareMPTID); env(vault.clawback({ .issuer = owner, .id = vaultKeylet.key, .holder = depositor, .amount = share(vaultShareBalance(vaultKeylet)).value(), }), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); env.close(); } { testcase("VaultClawback (share) - " + prefix + " owner can clawback own shares"); auto [vault, vaultKeylet] = setupVault(asset, owner, owner); auto const& vaultSle = env.le(vaultKeylet); - BEAST_EXPECT(vaultSle != nullptr); - if (!vaultSle) + if (!BEAST_EXPECT(vaultSle)) return; - Asset share = vaultSle->at(sfShareMPTID); + Asset const share = vaultSle->at(sfShareMPTID); env(vault.clawback({ .issuer = owner, .id = vaultKeylet.key, .holder = owner, .amount = share(vaultShareBalance(vaultKeylet)).value(), }), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); env.close(); } @@ -4831,44 +4777,43 @@ class Vault_test : public beast::unit_test::suite testcase("VaultClawback (share) - " + prefix + " empty vault share clawback fails"); auto [vault, vaultKeylet] = setupVault(asset, owner, owner); auto const& vaultSle = env.le(vaultKeylet); - if (BEAST_EXPECT(vaultSle != nullptr)) + if (!BEAST_EXPECT(vaultSle)) return; - Asset share = vaultSle->at(sfShareMPTID); + Asset const share = vaultSle->at(sfShareMPTID); env(vault.clawback({ .issuer = owner, .id = vaultKeylet.key, .holder = owner, .amount = share(vaultShareBalance(vaultKeylet)).value(), }), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); // Now the vault is empty, clawback again fails env(vault.clawback({ .issuer = owner, .id = vaultKeylet.key, .holder = owner, + .amount = share(vaultShareBalance(vaultKeylet)).value(), }), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); env.close(); } }; Account owner{"alice"}; Account depositor{"bob"}; - Account issuer{"issuer"}; + Account const issuer{"issuer"}; env.fund(XRP(10000), issuer, owner, depositor); env.close(); // Test XRP - PrettyAsset xrp = xrpIssue(); + PrettyAsset const xrp = xrpIssue(); testCase(xrp, "XRP", owner, depositor); testCase(xrp, "XRP (depositor is owner)", owner, owner); // Test IOU - PrettyAsset IOU = issuer["IOU"]; + PrettyAsset const IOU = issuer["IOU"]; env(fset(issuer, asfAllowTrustLineClawback)); env.close(); @@ -4883,7 +4828,7 @@ class Vault_test : public beast::unit_test::suite // Test MPT MPTTester mptt{env, issuer, mptInitNoFund}; mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); - PrettyAsset MPT = mptt.issuanceID(); + PrettyAsset const MPT = mptt.issuanceID(); mptt.authorize({.account = owner}); mptt.authorize({.account = depositor}); env(pay(issuer, owner, MPT(1000))); @@ -4900,23 +4845,24 @@ class Vault_test : public beast::unit_test::suite using namespace loanBroker; using namespace loan; Env env(*this); + env.enableFeature(fixSecurity3_1_3); auto const setupVault = [&](PrettyAsset const& asset, Account const& owner, Account const& depositor, Account const& issuer) -> std::pair { - Vault vault{env}; + Vault const vault{env}; auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset}); - env(tx, ter(tesSUCCESS), THISLINE); + env(tx, ter(tesSUCCESS)); env.close(); auto const& vaultSle = env.le(vaultKeylet); BEAST_EXPECT(vaultSle != nullptr); + env.memoize(Account("vault", vaultSle->at(sfAccount))); env(vault.deposit( {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); env.close(); return std::make_pair(vault, vaultKeylet); @@ -4939,16 +4885,14 @@ class Vault_test : public beast::unit_test::suite .holder = issuer, .amount = asset(1).value(), }), - ter(temMALFORMED), - THISLINE); + ter(temMALFORMED)); // When asset is implicit, clawback fails as no permission. env(vault.clawback({ .issuer = issuer, .id = vaultKeylet.key, .holder = issuer, }), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); return; } @@ -4957,16 +4901,15 @@ class Vault_test : public beast::unit_test::suite "VaultClawback (asset) - " + prefix + " clawback for different asset fails"); auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer); - Account issuer2{"issuer2"}; - PrettyAsset asset2 = issuer2["FOO"]; + Account const issuer2{"issuer2"}; + PrettyAsset const asset2 = issuer2["FOO"]; env(vault.clawback({ .issuer = issuer, .id = vaultKeylet.key, .holder = depositor, .amount = asset2(1).value(), }), - ter(tecWRONG_ASSET), - THISLINE); + ter(tecWRONG_ASSET)); } { @@ -4979,8 +4922,7 @@ class Vault_test : public beast::unit_test::suite .id = vaultKeylet.key, .holder = issuer, }), - ter(tecWRONG_ASSET), - THISLINE); + ter(tecWRONG_ASSET)); } { @@ -4992,8 +4934,7 @@ class Vault_test : public beast::unit_test::suite .id = vaultKeylet.key, .holder = depositor, }), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); env(vault.clawback({ .issuer = owner, @@ -5001,8 +4942,7 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = asset(1).value(), }), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); } { @@ -5013,18 +4953,16 @@ class Vault_test : public beast::unit_test::suite .id = vaultKeylet.key, .holder = issuer, }), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); } { testcase("VaultClawback (asset) - " + prefix + " issuer share clawback fails"); auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer); auto const& vaultSle = env.le(vaultKeylet); - BEAST_EXPECT(vaultSle != nullptr); - if (!vaultSle) + if (!BEAST_EXPECT(vaultSle)) return; - Asset share = vaultSle->at(sfShareMPTID); + Asset const share = vaultSle->at(sfShareMPTID); env(vault.clawback({ .issuer = issuer, @@ -5032,8 +4970,7 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = share(1).value(), }), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); } { @@ -5048,8 +4985,7 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = asset(1).value(), }), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); } { @@ -5063,8 +4999,7 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = asset(100).value(), }), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); } { @@ -5078,42 +5013,391 @@ class Vault_test : public beast::unit_test::suite .id = vaultKeylet.key, .holder = depositor, }), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); + } + + { + testcase( + "VaultClawback (asset) - " + prefix + + " zero-amount clawback clamped with outstanding loan"); + auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer); + + auto const vaultSle = env.le(vaultKeylet); + if (!BEAST_EXPECT(vaultSle)) + return; + + PrettyAsset const shares = MPTIssue(vaultSle->at(sfShareMPTID)); + + // Create a loan broker backed by this vault + auto const brokerKeylet = keylet::loanbroker(owner.id(), env.seq(owner)); + env(set(owner, vaultKeylet.key)); + env.close(); + + // Depositor borrows 40 units, reducing assetsAvailable to 60 + // while assetsTotal stays at 100 + env(set(depositor, brokerKeylet.key, asset(40).value()), + loan::interestRate(TenthBips32(0)), + gracePeriod(60), + paymentInterval(120), + paymentTotal(10), + sig(sfCounterpartySignature, owner), + fee(env.current()->fees().base * 2), + ter(tesSUCCESS)); + env.close(); + + { + auto const sle = env.le(vaultKeylet); + BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(60).value()); + BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(100).value()); + } + + // Zero-amount clawback (= "clawback all") should succeed, + // clamped to assetsAvailable (60) rather than the full + // share value (100). + env(vault.clawback({ + .issuer = issuer, + .id = vaultKeylet.key, + .holder = depositor, + }), + ter(tesSUCCESS)); + env.close(); + + // Only 60 assets clawed back; loan's 40 still outstanding + { + auto const sle = env.le(vaultKeylet); + BEAST_EXPECT(sle != nullptr); + BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(0).value()); + BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(40).value()); + + // 60 of 100 shares destroyed (1:1 ratio), 40 remain + auto const sharesAfter = env.balance(depositor, shares); + BEAST_EXPECT(sharesAfter == shares(Number{4, sle->at(sfScale) + 1})); + } + } + + { + testcase( + "VaultClawback (asset) - " + prefix + + " non-zero clawback clamped with outstanding loan"); + auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer); + + auto const vaultSle = env.le(vaultKeylet); + if (!BEAST_EXPECT(vaultSle)) + return; + PrettyAsset const shares = MPTIssue(vaultSle->at(sfShareMPTID)); + + // Create a loan broker backed by this vault + auto const brokerKeylet = keylet::loanbroker(owner.id(), env.seq(owner)); + env(set(owner, vaultKeylet.key)); + env.close(); + + // Depositor borrows 40 units + env(set(depositor, brokerKeylet.key, asset(40).value()), + loan::interestRate(TenthBips32(0)), + gracePeriod(60), + paymentInterval(120), + paymentTotal(10), + sig(sfCounterpartySignature, owner), + fee(env.current()->fees().base * 2), + ter(tesSUCCESS)); + env.close(); + + { + auto const sle = env.le(vaultKeylet); + BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(60).value()); + BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(100).value()); + } + + // Request 100 but only 60 available — clamped to 60 + env(vault.clawback({ + .issuer = issuer, + .id = vaultKeylet.key, + .holder = depositor, + .amount = asset(100).value(), + }), + ter(tesSUCCESS)); + env.close(); + + { + auto const sle = env.le(vaultKeylet); + BEAST_EXPECT(sle != nullptr); + BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(0).value()); + BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(40).value()); + + // 60 of 100 shares destroyed (1:1 ratio), 40 remain + auto const sharesAfter = env.balance(depositor, shares); + BEAST_EXPECT(sharesAfter == shares(Number{4, sle->at(sfScale) + 1})); + } + } + + { + testcase( + "VaultClawback (asset) - " + prefix + + " partial clawback below available with outstanding loan"); + auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer); + + auto const vaultSle = env.le(vaultKeylet); + if (!BEAST_EXPECT(vaultSle)) + return; + PrettyAsset const shares = MPTIssue(vaultSle->at(sfShareMPTID)); + + // Create a loan broker backed by this vault + auto const brokerKeylet = keylet::loanbroker(owner.id(), env.seq(owner)); + env(set(owner, vaultKeylet.key)); + env.close(); + + // Depositor borrows 40 units: assetsAvailable=60, assetsTotal=100 + env(set(depositor, brokerKeylet.key, asset(40).value()), + loan::interestRate(TenthBips32(0)), + gracePeriod(60), + paymentInterval(120), + paymentTotal(10), + sig(sfCounterpartySignature, owner), + fee(env.current()->fees().base * 2), + ter(tesSUCCESS)); + env.close(); + + { + auto const sle = env.le(vaultKeylet); + BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(60).value()); + BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(100).value()); + } + + // Clawback 30 — well under available (60), no clamping needed + env(vault.clawback({ + .issuer = issuer, + .id = vaultKeylet.key, + .holder = depositor, + .amount = asset(30).value(), + }), + ter(tesSUCCESS)); + env.close(); + + { + auto const sle = env.le(vaultKeylet); + BEAST_EXPECT(sle != nullptr); + BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(30).value()); + BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(70).value()); + + // 30 of 100 shares destroyed (1:1 ratio), 70 remain + auto const sharesAfter = env.balance(depositor, shares); + BEAST_EXPECT(sharesAfter == shares(Number{7, sle->at(sfScale) + 1})); + } + } + + { + testcase( + "VaultClawback (asset) - " + prefix + + " clawback exactly equal to available with outstanding loan"); + auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer); + + auto const vaultSle = env.le(vaultKeylet); + if (!BEAST_EXPECT(vaultSle)) + return; + PrettyAsset const shares = MPTIssue(vaultSle->at(sfShareMPTID)); + + auto const brokerKeylet = keylet::loanbroker(owner.id(), env.seq(owner)); + env(set(owner, vaultKeylet.key)); + env.close(); + + // Depositor borrows 40 units: assetsAvailable=60, assetsTotal=100 + env(set(depositor, brokerKeylet.key, asset(40).value()), + loan::interestRate(TenthBips32(0)), + gracePeriod(60), + paymentInterval(120), + paymentTotal(10), + sig(sfCounterpartySignature, owner), + fee(env.current()->fees().base * 2), + ter(tesSUCCESS)); + env.close(); + + // Clawback exactly 60 — at the boundary, no clamping needed + env(vault.clawback({ + .issuer = issuer, + .id = vaultKeylet.key, + .holder = depositor, + .amount = asset(60).value(), + }), + ter(tesSUCCESS)); + env.close(); + + { + auto const sle = env.le(vaultKeylet); + BEAST_EXPECT(sle != nullptr); + BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(0).value()); + BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(40).value()); + + // 60 of 100 shares destroyed (1:1 ratio), 40 remain + auto const sharesAfter = env.balance(depositor, shares); + BEAST_EXPECT(sharesAfter == shares(Number{4, sle->at(sfScale) + 1})); + } + } + + { + testcase( + "VaultClawback (asset) - " + prefix + + " clawback with zero available (fully borrowed)"); + auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer); + + auto const vaultSle = env.le(vaultKeylet); + if (!BEAST_EXPECT(vaultSle)) + return; + PrettyAsset const shares = MPTIssue(vaultSle->at(sfShareMPTID)); + + auto const brokerKeylet = keylet::loanbroker(owner.id(), env.seq(owner)); + env(set(owner, vaultKeylet.key)); + env.close(); + + // Depositor borrows all 100 units: assetsAvailable=0, assetsTotal=100 + env(set(depositor, brokerKeylet.key, asset(100).value()), + loan::interestRate(TenthBips32(0)), + gracePeriod(60), + paymentInterval(120), + paymentTotal(10), + sig(sfCounterpartySignature, owner), + fee(env.current()->fees().base * 2), + ter(tesSUCCESS)); + env.close(); + + { + auto const sle = env.le(vaultKeylet); + BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(0).value()); + BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(100).value()); + } + + auto const sharesBefore = env.balance(depositor, shares); + + // Zero-amount clawback — nothing available, clamped to 0, + // resulting in zero shares destroyed → tecPRECISION_LOSS + env(vault.clawback({ + .issuer = issuer, + .id = vaultKeylet.key, + .holder = depositor, + }), + ter(tecPRECISION_LOSS)); + env.close(); + + // Explicit amount clawback — also nothing available + env(vault.clawback({ + .issuer = issuer, + .id = vaultKeylet.key, + .holder = depositor, + .amount = asset(50).value(), + }), + ter(tecPRECISION_LOSS)); + env.close(); + + { + // Nothing changed — vault and shares unchanged + auto const sle = env.le(vaultKeylet); + BEAST_EXPECT(sle != nullptr); + BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(0).value()); + BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(100).value()); + auto const sharesAfter = env.balance(depositor, shares); + BEAST_EXPECT(sharesAfter == sharesBefore); + } } }; Account owner{"alice"}; Account depositor{"bob"}; - Account issuer{"issuer"}; + Account const issuer{"issuer"}; env.fund(XRP(10000), issuer, owner, depositor); env.close(); // Test XRP - PrettyAsset xrp = xrpIssue(); + PrettyAsset const xrp = xrpIssue(); testCase(xrp, "XRP", owner, depositor, issuer); // Test IOU - PrettyAsset IOU = issuer["IOU"]; + PrettyAsset const IOU = issuer["IOU"]; env(fset(issuer, asfAllowTrustLineClawback)); env.close(); - env.trust(IOU(1000), owner); - env.trust(IOU(1000), depositor); - env(pay(issuer, owner, IOU(1000))); - env(pay(issuer, depositor, IOU(1000))); + env.trust(IOU(2000), owner); + env.trust(IOU(2000), depositor); + env(pay(issuer, owner, IOU(2000))); + env(pay(issuer, depositor, IOU(2000))); env.close(); testCase(IOU, "IOU", owner, depositor, issuer); // Test MPT MPTTester mptt{env, issuer, mptInitNoFund}; mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); - PrettyAsset MPT = mptt.issuanceID(); + PrettyAsset const MPT = mptt.issuanceID(); mptt.authorize({.account = owner}); mptt.authorize({.account = depositor}); - env(pay(issuer, depositor, MPT(1000))); + env(pay(issuer, depositor, MPT(2000))); env.close(); testCase(MPT, "MPT", owner, depositor, issuer); + + // Test pre-fixSecurity3_1_3 legacy path: zero-amount clawback + // returns early without clamping to assetsAvailable. + { + testcase( + "VaultClawback (asset) - IOU pre-fixSecurity3_1_3" + " zero-amount clawback unclamped with outstanding loan"); + + env.disableFeature(fixSecurity3_1_3); + + auto [vault, vaultKeylet] = setupVault(IOU, owner, depositor, issuer); + + auto const vaultSle = env.le(vaultKeylet); + BEAST_EXPECT(vaultSle != nullptr); + if (!vaultSle) + return; + + PrettyAsset const shares = MPTIssue(vaultSle->at(sfShareMPTID)); + + // Create a loan broker backed by this vault + auto const brokerKeylet = keylet::loanbroker(owner.id(), env.seq(owner)); + env(set(owner, vaultKeylet.key)); + env.close(); + + // Depositor borrows 40 units, reducing assetsAvailable to 60 + // while assetsTotal stays at 100 + env(set(depositor, brokerKeylet.key, IOU(40).value()), + loan::interestRate(TenthBips32(0)), + gracePeriod(60), + paymentInterval(120), + paymentTotal(10), + sig(sfCounterpartySignature, owner), + fee(env.current()->fees().base * 2), + ter(tesSUCCESS)); + env.close(); + + { + auto const sle = env.le(vaultKeylet); + BEAST_EXPECT(sle->at(sfAssetsAvailable) == IOU(60).value()); + BEAST_EXPECT(sle->at(sfAssetsTotal) == IOU(100).value()); + } + + auto const sharesBefore = env.balance(depositor, shares); + + // Legacy: zero-amount clawback tries to recover the full + // share value (100) without clamping to assetsAvailable (60). + // This causes the vault balance to go negative, triggering + // the sanity check in doApply → tefINTERNAL. + env(vault.clawback({ + .issuer = issuer, + .id = vaultKeylet.key, + .holder = depositor, + }), + ter(tefINTERNAL)); + env.close(); + + { + // Transaction rolled back — vault and shares unchanged + auto const sle = env.le(vaultKeylet); + BEAST_EXPECT(sle != nullptr); + BEAST_EXPECT(sle->at(sfAssetsAvailable) == IOU(60).value()); + BEAST_EXPECT(sle->at(sfAssetsTotal) == IOU(100).value()); + auto const sharesAfter = env.balance(depositor, shares); + BEAST_EXPECT(sharesAfter == sharesBefore); + } + + env.enableFeature(fixSecurity3_1_3); + } } void @@ -5127,7 +5411,7 @@ class Vault_test : public beast::unit_test::suite Account const owner{"owner"}; Account const issuer{"issuer"}; - Vault vault{env}; + Vault const vault{env}; env.fund(XRP(1'000'000), issuer, owner); env.close(); @@ -5154,19 +5438,19 @@ class Vault_test : public beast::unit_test::suite tx[sfData] = "4D65746144617461"; tx[sfAssetsMaximum] = maxInt64; - env(tx, ter(tefEXCEPTION), THISLINE); + env(tx, ter(tefEXCEPTION)); env.close(); tx[sfAssetsMaximum] = initialXRPPlus1; - env(tx, ter(tefEXCEPTION), THISLINE); + env(tx, ter(tefEXCEPTION)); env.close(); tx[sfAssetsMaximum] = initialXRP; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = maxInt64Plus1; - env(tx, ter(tefEXCEPTION), THISLINE); + env(tx, ter(tefEXCEPTION)); env.close(); // This value will be rounded @@ -5176,7 +5460,7 @@ class Vault_test : public beast::unit_test::suite BEAST_EXPECT(decimalTest == "9223372036854775.808"); tx[sfAssetsMaximum] = decimalTest; auto const newKeylet = keylet::vault(owner.id(), env.seq(owner)); - env(tx, THISLINE); + env(tx); env.close(); auto const vaultSle = env.le(newKeylet); @@ -5199,26 +5483,26 @@ class Vault_test : public beast::unit_test::suite return mptAsset; }(); - env(pay(issuer, owner, mptAsset(100'000)), THISLINE); + env(pay(issuer, owner, mptAsset(100'000))); env.close(); auto [tx, keylet] = vault.create({.owner = owner, .asset = mptAsset}); tx[sfData] = "4D65746144617461"; tx[sfAssetsMaximum] = maxInt64; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = initialXRPPlus1; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = initialXRP; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = maxInt64Plus1; - env(tx, ter(tefEXCEPTION), THISLINE); + env(tx, ter(tefEXCEPTION)); env.close(); // This value will be rounded @@ -5228,7 +5512,7 @@ class Vault_test : public beast::unit_test::suite BEAST_EXPECT(decimalTest == "922337203685477580.8"); tx[sfAssetsMaximum] = decimalTest; auto const newKeylet = keylet::vault(owner.id(), env.seq(owner)); - env(tx, THISLINE); + env(tx); env.close(); auto const vaultSle = env.le(newKeylet); @@ -5242,7 +5526,7 @@ class Vault_test : public beast::unit_test::suite testcase("Assets Maximum: IOU"); // Almost anything goes with IOUs - PrettyAsset iouAsset = issuer["IOU"]; + PrettyAsset const iouAsset = issuer["IOU"]; env.trust(iouAsset(1000), owner); env(pay(issuer, owner, iouAsset(200))); env.close(); @@ -5251,19 +5535,19 @@ class Vault_test : public beast::unit_test::suite tx[sfData] = "4D65746144617461"; tx[sfAssetsMaximum] = maxInt64; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = initialXRPPlus1; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = initialXRP; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = maxInt64Plus1; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = "1000000000000000e80"; @@ -5280,7 +5564,7 @@ class Vault_test : public beast::unit_test::suite BEAST_EXPECT(decimalTest == "922337203685477580.8"); tx[sfAssetsMaximum] = decimalTest; auto const newKeylet = keylet::vault(owner.id(), env.seq(owner)); - env(tx, THISLINE); + env(tx); env.close(); auto const vaultSle = env.le(newKeylet); @@ -5294,7 +5578,7 @@ class Vault_test : public beast::unit_test::suite { tx[sfAssetsMaximum] = "9223372036854775807e40"; // max int64 * 10^40 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner)); - env(tx, THISLINE); + env(tx); env.close(); auto const vaultSle = env.le(newKeylet); @@ -5308,7 +5592,7 @@ class Vault_test : public beast::unit_test::suite { tx[sfAssetsMaximum] = "9223372036854775807e-40"; // max int64 * 10^-40 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner)); - env(tx, THISLINE); + env(tx); env.close(); auto const vaultSle = env.le(newKeylet); @@ -5322,7 +5606,7 @@ class Vault_test : public beast::unit_test::suite { tx[sfAssetsMaximum] = "9223372036854775807e-100"; // max int64 * 10^-100 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner)); - env(tx, THISLINE); + env(tx); env.close(); // Field 'AssetsMaximum' may not be explicitly set to default. @@ -5336,7 +5620,7 @@ class Vault_test : public beast::unit_test::suite // What _can't_ IOUs do? // 1. Exceed maximum exponent / offset tx[sfAssetsMaximum] = "1000000000000000e81"; - env(tx, ter(tefEXCEPTION), THISLINE); + env(tx, ter(tefEXCEPTION)); env.close(); // 2. Mantissa larger than uint64 max @@ -5344,7 +5628,7 @@ class Vault_test : public beast::unit_test::suite try { tx[sfAssetsMaximum] = "18446744073709551617e5"; // uint64 max + 1 - env(tx, THISLINE); + env(tx); BEAST_EXPECTS(false, "Expected parse_error for mantissa larger than uint64 max"); } catch (parse_error const& e) @@ -5357,6 +5641,434 @@ class Vault_test : public beast::unit_test::suite } } + void + testVaultEscrowedMPT() + { + using namespace test::jtx; + using namespace std::literals; + + // Verify vault deposit/withdraw/clawback respect sfLockedAmount. + // When MPT tokens are escrowed, sfMPTAmount is reduced and + // sfLockedAmount is increased. Vault operations go through + // accountSend/accountHolds which read sfMPTAmount, so escrowed + // tokens are naturally excluded. + + { + testcase("Vault deposit fails when MPT asset is escrowed"); + + Env env{*this, testable_amendments()}; + auto const baseFee = env.current()->fees().base; + Account const owner{"owner"}; + Account const depositor{"depositor"}; + Account const issuer{"issuer"}; + Account const bob{"bob"}; + + env.fund(XRP(10000), issuer, owner, depositor, bob); + env.close(); + + MPTTester mptt{env, issuer, mptInitNoFund}; + mptt.create( + {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock | tfMPTCanEscrow}); + mptt.authorize({.account = owner}); + mptt.authorize({.account = depositor}); + mptt.authorize({.account = bob}); + PrettyAsset const asset = mptt.issuanceID(); + env(pay(issuer, depositor, asset(100))); + env.close(); + + // Escrow 60 of 100 MPT tokens: sfMPTAmount drops to 40 + auto const escrowSeq = env.seq(depositor); + env(escrow::create(depositor, bob, asset(60)), + escrow::condition(escrow::cb1), + escrow::finish_time(env.now() + 1s), + fee(baseFee * 150), + ter(tesSUCCESS)); + env.close(); + + Vault const vault{env}; + auto [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset}); + env(tx, ter(tesSUCCESS)); + env.close(); + + // Deposit 100 should fail — only 40 spendable + env(vault.deposit( + {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}), + ter(tecINSUFFICIENT_FUNDS)); + env.close(); + + // Deposit 40 (the unlocked balance) should succeed + env(vault.deposit({.depositor = depositor, .id = vaultKeylet.key, .amount = asset(40)}), + ter(tesSUCCESS)); + env.close(); + + { + auto const sle = env.le(vaultKeylet); + BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(40).value()); + } + + // Clean up escrow + env(escrow::finish(bob, depositor, escrowSeq), + escrow::condition(escrow::cb1), + escrow::fulfillment(escrow::fb1), + fee(baseFee * 150), + ter(tesSUCCESS)); + env.close(); + } + + { + testcase("Vault withdraw respects escrowed shares"); + + Env env{*this, testable_amendments()}; + auto const baseFee = env.current()->fees().base; + Account const owner{"owner"}; + Account const depositor{"depositor"}; + Account const issuer{"issuer"}; + Account const bob{"bob"}; + + env.fund(XRP(10000), issuer, owner, depositor, bob); + env.close(); + + MPTTester mptt{env, issuer, mptInitNoFund}; + mptt.create( + {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock | tfMPTCanEscrow}); + mptt.authorize({.account = owner}); + mptt.authorize({.account = depositor}); + PrettyAsset const asset = mptt.issuanceID(); + env(pay(issuer, depositor, asset(100))); + env.close(); + + Vault const vault{env}; + auto [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset}); + env(tx, ter(tesSUCCESS)); + env.close(); + + // Deposit 100 → get shares + env(vault.deposit( + {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}), + ter(tesSUCCESS)); + env.close(); + + auto const vaultSle = env.le(vaultKeylet); + if (!BEAST_EXPECT(vaultSle)) + return; + env.memoize(Account("vault", vaultSle->at(sfAccount))); + PrettyAsset const shares = MPTIssue(vaultSle->at(sfShareMPTID)); + + // Authorize bob for share MPT so he can receive escrowed shares + auto const shareMPTID = vaultSle->at(sfShareMPTID); + { + Json::Value jv; + jv[jss::Account] = bob.human(); + jv[sfMPTokenIssuanceID] = to_string(shareMPTID); + jv[jss::TransactionType] = jss::MPTokenAuthorize; + env(jv, ter(tesSUCCESS)); + env.close(); + } + + // Escrow 60% of shares + auto const escrowAmount = shares(Number{6, vaultSle->at(sfScale) + 1}); + env(escrow::create(depositor, bob, escrowAmount), + escrow::condition(escrow::cb1), + escrow::finish_time(env.now() + 1s), + fee(baseFee * 150), + ter(tesSUCCESS)); + env.close(); + + // Withdraw all 100 should fail — only 40% of shares are unlocked + env(vault.withdraw( + {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}), + ter(tecINSUFFICIENT_FUNDS)); + env.close(); + + // Withdraw 40 (matching unlocked shares) should succeed + env(vault.withdraw( + {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(40)}), + ter(tesSUCCESS)); + env.close(); + + { + auto const sle = env.le(vaultKeylet); + BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(60).value()); + } + } + + { + testcase("Vault clawback only recovers unlocked shares"); + + Env env{*this, testable_amendments() | fixSecurity3_1_3}; + auto const baseFee = env.current()->fees().base; + Account const owner{"owner"}; + Account const depositor{"depositor"}; + Account const issuer{"issuer"}; + Account const bob{"bob"}; + + env.fund(XRP(10000), issuer, owner, depositor, bob); + env.close(); + + MPTTester mptt{env, issuer, mptInitNoFund}; + mptt.create( + {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock | tfMPTCanEscrow}); + mptt.authorize({.account = owner}); + mptt.authorize({.account = depositor}); + PrettyAsset const asset = mptt.issuanceID(); + env(pay(issuer, depositor, asset(100))); + env.close(); + + Vault const vault{env}; + auto [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset}); + env(tx, ter(tesSUCCESS)); + env.close(); + + // Deposit 100 → get shares + env(vault.deposit( + {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}), + ter(tesSUCCESS)); + env.close(); + + auto const vaultSle = env.le(vaultKeylet); + if (!BEAST_EXPECT(vaultSle)) + return; + env.memoize(Account("vault", vaultSle->at(sfAccount))); + PrettyAsset const shares = MPTIssue(vaultSle->at(sfShareMPTID)); + + // Authorize bob for share MPT so he can receive escrowed shares + auto const shareMPTID = vaultSle->at(sfShareMPTID); + { + Json::Value jv; + jv[jss::Account] = bob.human(); + jv[sfMPTokenIssuanceID] = to_string(shareMPTID); + jv[jss::TransactionType] = jss::MPTokenAuthorize; + env(jv, ter(tesSUCCESS)); + env.close(); + } + + // Escrow 60% of shares + auto const escrowAmount = shares(Number{6, vaultSle->at(sfScale) + 1}); + env(escrow::create(depositor, bob, escrowAmount), + escrow::condition(escrow::cb1), + escrow::finish_time(env.now() + 1s), + fee(baseFee * 150), + ter(tesSUCCESS)); + env.close(); + + // Zero-amount clawback ("all") — should only recover assets + // corresponding to unlocked shares (40%) + env(vault.clawback({ + .issuer = issuer, + .id = vaultKeylet.key, + .holder = depositor, + }), + ter(tesSUCCESS)); + env.close(); + + { + auto const sle = env.le(vaultKeylet); + BEAST_EXPECT(sle != nullptr); + // Only 40 of 100 assets recovered (matching 40% unlocked shares) + BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(60).value()); + BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(60).value()); + + // Depositor's unlocked shares are now 0 + auto const sharesAfter = env.balance(depositor, shares); + BEAST_EXPECT(sharesAfter == shares(0)); + } + } + } + + // Reproduction: canWithdraw IOU limit check bypassed when + // withdrawal amount is specified in shares (MPT) rather than in assets. + void + testBug6_LimitBypassWithShares() + { + using namespace test::jtx; + testcase("Bug6 - limit bypass with share-denominated withdrawal"); + + auto const allAmendments = testable_amendments() | featureSingleAssetVault; + + for (auto const& features : {allAmendments, allAmendments - fixSecurity3_1_3}) + { + bool const withFix = features[fixSecurity3_1_3]; + + Env env{*this, features}; + Account const owner{"owner"}; + Account const issuer{"issuer"}; + Account const depositor{"depositor"}; + Account const charlie{"charlie"}; + Vault const vault{env}; + + env.fund(XRP(1000), issuer, owner, depositor, charlie); + env(fset(issuer, asfAllowTrustLineClawback)); + env.close(); + + PrettyAsset const asset = issuer["IOU"]; + env.trust(asset(1000), owner); + env.trust(asset(1000), depositor); + env(pay(issuer, owner, asset(200))); + env(pay(issuer, depositor, asset(200))); + env.close(); + + // Charlie gets a LOW trustline limit of 5 + env.trust(asset(5), charlie); + env.close(); + + auto const [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + auto const depositTx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); + env(depositTx); + env.close(); + + // Get the share MPT info + auto const vaultSle = env.le(keylet); + if (!BEAST_EXPECT(vaultSle)) + return; + auto const mptIssuanceID = vaultSle->at(sfShareMPTID); + MPTIssue const shares(mptIssuanceID); + PrettyAsset const share(shares); + + // CONTROL: Withdraw 10 IOU (asset-denominated) to charlie. + // Charlie's limit is 5, so this should be rejected with tecNO_LINE + // regardless of the amendment. + { + auto withdrawTx = + vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(10)}); + withdrawTx[sfDestination] = charlie.human(); + env(withdrawTx, ter{tecNO_LINE}); + env.close(); + } + auto const charlieBalanceBefore = env.balance(charlie, asset.raw().get()); + + // Withdraw the equivalent amount in shares to charlie. + // Post-fix: rejected (tecNO_LINE) because the share amount is + // converted to assets and the trustline limit is checked. + // Pre-fix: succeeds (tesSUCCESS) because the limit check was + // skipped for share-denominated withdrawals. + { + auto withdrawTx = vault.withdraw( + {.depositor = depositor, + .id = keylet.key, + .amount = STAmount(share, 10'000'000)}); + withdrawTx[sfDestination] = charlie.human(); + env(withdrawTx, ter{withFix ? TER{tecNO_LINE} : TER{tesSUCCESS}}); + env.close(); + + auto const charlieBalanceAfter = env.balance(charlie, asset.raw().get()); + if (withFix) + { + // Post-fix: charlie's balance is unchanged — the withdrawal + // was correctly rejected despite being share-denominated. + BEAST_EXPECT(charlieBalanceAfter == charlieBalanceBefore); + } + else + { + // Pre-fix: charlie received the assets, bypassing the + // trustline limit. + BEAST_EXPECT(charlieBalanceAfter > charlieBalanceBefore); + } + } + } + } + + void + testRemoveEmptyHoldingLockedAmount() + { + testcase("removeEmptyHolding deletes MPToken with sfLockedAmount"); + using namespace test::jtx; + using namespace std::literals; + + auto const amendments = testable_amendments(); + auto runTest = [&](FeatureBitset f) { + Env env{*this, f}; + auto const baseFee = env.current()->fees().base; + + Account const issuer{"issuer"}; + Account const owner{"owner"}; + Account const depositor{"depositor"}; + Account const bob{"bob"}; + + env.fund(XRP(100000), issuer, owner, depositor, bob); + env.close(); + + Vault const vault{env}; + + // Create an MPT asset for the vault + MPTTester mptt{env, issuer, mptInitNoFund}; + mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock}); + PrettyAsset const asset = mptt.issuanceID(); + mptt.authorize({.account = owner}); + mptt.authorize({.account = depositor}); + env(pay(issuer, depositor, asset(1000))); + env.close(); + + // Create vault + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + auto const vaultSle = env.le(keylet); + BEAST_EXPECT(vaultSle != nullptr); + auto const shareMptID = vaultSle->at(sfShareMPTID); + MPTIssue const shareIssue{shareMptID}; + + // Depositor deposits 1000 asset units into vault, receiving shares + env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1000)})); + env.close(); + + // Check depositor has shares + { + auto const sleMpt = env.le(keylet::mptoken(shareMptID, depositor)); + BEAST_EXPECT(sleMpt != nullptr); + BEAST_EXPECT(sleMpt->at(sfMPTAmount) == 1000); + } + + // Escrow 500 of those shares + env(escrow::create(depositor, bob, STAmount{shareIssue, 500}), + escrow::condition(escrow::cb1), + escrow::finish_time(env.now() + 1s), + fee(baseFee * 150), + ter(tesSUCCESS)); + env.close(); + + // Verify: sfMPTAmount=500, sfLockedAmount=500 + { + auto const sleMpt = env.le(keylet::mptoken(shareMptID, depositor)); + BEAST_EXPECT(sleMpt != nullptr); + BEAST_EXPECT(sleMpt->at(sfLockedAmount) == 500); + BEAST_EXPECT(sleMpt->at(sfMPTAmount) == 500); + } + + // Withdraw remaining spendable shares — triggers removeEmptyHolding + env(vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(500)}), + ter(tesSUCCESS)); + env.close(); + + auto const sleMptAfter = env.le(keylet::mptoken(shareMptID, depositor)); + if (!f[fixSecurity3_1_3]) + { + // Without the fix, removeEmptyHolding deletes the MPToken + // even though sfLockedAmount > 0, leaving the escrow's locked + // amount untracked. + BEAST_EXPECT(sleMptAfter == nullptr); + } + else + { + // With the fix, MPToken must still exist with sfLockedAmount > 0 + // and sfMPTAmount == 0 (all spendable shares withdrawn). + BEAST_EXPECT(sleMptAfter != nullptr); + if (sleMptAfter) + { + BEAST_EXPECT(sleMptAfter->at(sfLockedAmount) == 500); + BEAST_EXPECT(sleMptAfter->at(sfMPTAmount) == 0); + } + } + }; + + runTest(amendments - fixSecurity3_1_3); + runTest(amendments); + } + public: void run() override @@ -5374,10 +6086,12 @@ public: testFailedPseudoAccount(); testScaleIOU(); testRPC(); - testDelegate(); testVaultClawbackBurnShares(); testVaultClawbackAssets(); + testVaultEscrowedMPT(); testAssetsMaximum(); + testBug6_LimitBypassWithShares(); + testRemoveEmptyHoldingLockedAmount(); } }; diff --git a/src/test/app/XChain_test.cpp b/src/test/app/XChain_test.cpp index c74857fa86..abaeccb4ff 100644 --- a/src/test/app/XChain_test.cpp +++ b/src/test/app/XChain_test.cpp @@ -84,7 +84,7 @@ struct SEnv template SEnv& - multiTx(jtx::JValueVec&& jvv, FN const&... fN) + multiTx(jtx::JValueVec const& jvv, FN const&... fN) { for (auto const& jv : jvv) env_(jv, fN...); @@ -130,7 +130,7 @@ struct SEnv std::shared_ptr bridge(Json::Value const& jvb) { - STXChainBridge b(jvb); + STXChainBridge const b(jvb); auto tryGet = [&](STXChainBridge::ChainType ct) -> std::shared_ptr { if (auto r = env_.le(keylet::bridge(b, ct))) @@ -180,7 +180,7 @@ struct XEnv : public jtx::XChainBridgeObjects, public SEnv XEnv(T& s, bool side = false) : SEnv(s, jtx::envconfig(), features) { using namespace jtx; - STAmount xrp_funds{XRP(10000)}; + STAmount const xrp_funds{XRP(10000)}; if (!side) { @@ -359,7 +359,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj try { exceptionPresent = false; - [[maybe_unused]] STXChainBridge testBridge1(jBridge); + [[maybe_unused]] STXChainBridge const testBridge1(jBridge); } catch (std::exception& ec) { @@ -372,7 +372,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj { exceptionPresent = false; jBridge["Extra"] = 1; - [[maybe_unused]] STXChainBridge testBridge2(jBridge); + [[maybe_unused]] STXChainBridge const testBridge2(jBridge); } catch ([[maybe_unused]] std::exception& ec) { @@ -385,7 +385,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj void testXChainCreateBridge() { - XRPAmount res1 = reserve(1); + XRPAmount const res1 = reserve(1); using namespace jtx; testcase("Create Bridge"); @@ -807,12 +807,12 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj auto const& expected = expected_result[test_result.size()]; mcEnv.tx(create_bridge(a, bridge(a, ia, b, ib)), ter(TER::fromInt(expected.first))); - TER mcTER = mcEnv.env_.ter(); + TER const mcTER = mcEnv.env_.ter(); scEnv.tx(create_bridge(b, bridge(a, ia, b, ib)), ter(TER::fromInt(expected.second))); - TER scTER = scEnv.env_.ter(); + TER const scTER = scEnv.env_.ter(); - bool pass = mcTER == tesSUCCESS && scTER == tesSUCCESS; + bool const pass = isTesSuccess(mcTER) && isTesSuccess(scTER); test_result.emplace_back(mcTER, scTER, pass); }; @@ -1155,8 +1155,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj testXChainCreateClaimID() { using namespace jtx; - XRPAmount res1 = reserve(1); - XRPAmount tx_fee = txFee(); + XRPAmount const res1 = reserve(1); + XRPAmount const tx_fee = txFee(); testcase("Create ClaimID"); @@ -1173,7 +1173,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj { XEnv xenv(*this, true); - Balance scAlice_bal(xenv, scAlice); + Balance const scAlice_bal(xenv, scAlice); xenv.tx(create_bridge(Account::master, jvb)) .tx(xchain_create_claim_id(scAlice, jvb, reward, mcAlice)) @@ -1240,8 +1240,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj testXChainCommit() { using namespace jtx; - XRPAmount res0 = reserve(0); - XRPAmount tx_fee = txFee(); + XRPAmount const res0 = reserve(0); + XRPAmount const tx_fee = txFee(); testcase("Commit"); @@ -1252,7 +1252,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj { XEnv xenv(*this); - Balance alice_bal(xenv, mcAlice); + Balance const alice_bal(xenv, mcAlice); auto const amt = XRP(1000); xenv.tx(create_bridge(mcDoor, jvb)) @@ -1260,7 +1260,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .tx(xchain_commit(mcAlice, jvb, 1, amt, scBob)) .close(); - STAmount claim_cost = amt; + STAmount const claim_cost = amt; BEAST_EXPECT(alice_bal.diff() == -(claim_cost + tx_fee)); } @@ -1369,7 +1369,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj using namespace jtx; testcase("Add Attestation"); - XRPAmount res0 = reserve(0); + XRPAmount const res0 = reserve(0); XRPAmount tx_fee = txFee(); auto multiTtxFee = [&](std::uint32_t m) -> STAmount { @@ -1457,7 +1457,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj std::uint32_t const quorum_7 = 7; std::vector const signers_ = [] { constexpr int numSigners = 4; - std::uint32_t weights[] = {1, 2, 4, 4}; + std::uint32_t const weights[] = {1, 2, 4, 4}; std::vector result; result.reserve(numSigners); @@ -1515,7 +1515,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj std::uint32_t const quorum_7 = 7; std::vector const signers_ = [] { constexpr int numSigners = 4; - std::uint32_t weights[] = {1, 2, 4, 4}; + std::uint32_t const weights[] = {1, 2, 4, 4}; std::vector result; result.reserve(numSigners); @@ -1575,7 +1575,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj std::uint32_t const quorum_7 = 7; std::vector const signers_ = [] { constexpr int numSigners = 4; - std::uint32_t weights[] = {1, 2, 4, 4}; + std::uint32_t const weights[] = {1, 2, 4, 4}; std::vector result; result.reserve(numSigners); @@ -1634,7 +1634,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj std::uint32_t const quorum_7 = 7; std::vector const signers_ = [] { constexpr int numSigners = 4; - std::uint32_t weights[] = {1, 2, 4, 4}; + std::uint32_t const weights[] = {1, 2, 4, 4}; std::vector result; result.reserve(numSigners); @@ -1697,8 +1697,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj auto const amt_plus_reward = amt + reward; { - Balance door(mcEnv, mcDoor); - Balance carol(mcEnv, mcCarol); + Balance const door(mcEnv, mcDoor); + Balance const carol(mcEnv, mcCarol); mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))) .close() @@ -1719,8 +1719,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj { // send first batch of account create attest for all 3 // account create - Balance attester(scEnv, scAttester); - Balance door(scEnv, Account::master); + Balance const attester(scEnv, scAttester); + Balance const door(scEnv, Account::master); scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2)) .multiTx(att_create_acct_vec(3, amt, scuCarol, 2)) @@ -1740,8 +1740,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj { // complete attestations for 2nd account create => should // not complete - Balance attester(scEnv, scAttester); - Balance door(scEnv, Account::master); + Balance const attester(scEnv, scAttester); + Balance const door(scEnv, Account::master); scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 3, 2)).close(); @@ -1756,8 +1756,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj { // complete attestations for 3rd account create => should // not complete - Balance attester(scEnv, scAttester); - Balance door(scEnv, Account::master); + Balance const attester(scEnv, scAttester); + Balance const door(scEnv, Account::master); scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 3, 2)).close(); @@ -1772,8 +1772,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj { // complete attestations for 1st account create => account // should be created - Balance attester(scEnv, scAttester); - Balance door(scEnv, Account::master); + Balance const attester(scEnv, scAttester); + Balance const door(scEnv, Account::master); scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 3, 1)).close(); @@ -1791,8 +1791,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj { // resend attestations for 3rd account create => still // should not complete - Balance attester(scEnv, scAttester); - Balance door(scEnv, Account::master); + Balance const attester(scEnv, scAttester); + Balance const door(scEnv, Account::master); scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 3, 2)).close(); @@ -1808,8 +1808,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj { // resend attestations for 2nd account create => account // should be created - Balance attester(scEnv, scAttester); - Balance door(scEnv, Account::master); + Balance const attester(scEnv, scAttester); + Balance const door(scEnv, Account::master); scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 1)).close(); @@ -1824,8 +1824,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj { // resend attestations for 3rc account create => account // should be created - Balance attester(scEnv, scAttester); - Balance door(scEnv, Account::master); + Balance const attester(scEnv, scAttester); + Balance const door(scEnv, Account::master); scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1)).close(); @@ -1850,8 +1850,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close(); { - Balance door(mcEnv, mcDoor); - Balance carol(mcEnv, mcCarol); + Balance const door(mcEnv, mcDoor); + Balance const carol(mcEnv, mcCarol); mcEnv.tx(sidechain_xchain_account_create(mcCarol, jvb, scuAlice, amt, reward)) .close(); @@ -1864,8 +1864,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .tx(jtx::signers(Account::master, quorum, signers)) .close(); - Balance attester(scEnv, scAttester); - Balance door(scEnv, Account::master); + Balance const attester(scEnv, scAttester); + Balance const door(scEnv, Account::master); scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2)).close(); BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present @@ -1892,8 +1892,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close(); { - Balance door(mcEnv, mcDoor); - Balance carol(mcEnv, mcCarol); + Balance const door(mcEnv, mcDoor); + Balance const carol(mcEnv, mcCarol); mcEnv.tx(sidechain_xchain_account_create(mcCarol, jvb, scAlice, amt, reward)) .close(); @@ -1906,9 +1906,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .tx(jtx::signers(Account::master, quorum, signers)) .close(); - Balance attester(scEnv, scAttester); - Balance door(scEnv, Account::master); - Balance alice(scEnv, scAlice); + Balance const attester(scEnv, scAttester); + Balance const door(scEnv, Account::master); + Balance const alice(scEnv, scAlice); scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2)).close(); BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present @@ -1935,8 +1935,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close(); { - Balance door(mcEnv, mcDoor); - Balance carol(mcEnv, mcCarol); + Balance const door(mcEnv, mcDoor); + Balance const carol(mcEnv, mcCarol); mcEnv.tx(sidechain_xchain_account_create(mcCarol, jvb, scAlice, amt, reward)) .close(); @@ -1950,9 +1950,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .tx(fset("scAlice", asfDepositAuth)) // set deposit auth .close(); - Balance attester(scEnv, scAttester); - Balance door(scEnv, Account::master); - Balance alice(scEnv, scAlice); + Balance const attester(scEnv, scAttester); + Balance const door(scEnv, Account::master); + Balance const alice(scEnv, scAlice); scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2)).close(); BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present @@ -1979,8 +1979,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj auto const amt_plus_reward = amt + reward; { - Balance door(mcEnv, mcDoor); - Balance carol(mcEnv, mcCarol); + Balance const door(mcEnv, mcDoor); + Balance const carol(mcEnv, mcCarol); mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))) .close() @@ -2002,8 +2002,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .close(); { - Balance attester(scEnv, scAttester); - Balance door(scEnv, Account::master); + Balance const attester(scEnv, scAttester); + Balance const door(scEnv, Account::master); auto const bad_amt = XRP(10); std::uint32_t txCount = 0; @@ -2142,14 +2142,22 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj TER const expectedTER = i < quorum ? tesSUCCESS : TER{tecXCHAIN_NO_CLAIM_ID}; if (i + 1 == quorum) + { scEnv.tx(att, ter(expectedTER)).close(); + } else + { scEnv.tx(att, ter(expectedTER)).close(); + } if (i + 1 < quorum) + { BEAST_EXPECT(dstStartBalance == scEnv.env_.balance(dst)); + } else + { BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst)); + } } BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst)); } @@ -2282,16 +2290,16 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj XEnv mcEnv(*this); XEnv scEnv(*this, true); - XRPAmount tx_fee = mcEnv.txFee(); + XRPAmount const tx_fee = mcEnv.txFee(); - Account a{"a"}; - Account doorA{"doorA"}; + Account const a{"a"}; + Account const doorA{"doorA"}; - STAmount funds{XRP(10000)}; + STAmount const funds{XRP(10000)}; mcEnv.fund(funds, a); mcEnv.fund(funds, doorA); - Account ua{"ua"}; // unfunded account we want to create + Account const ua{"ua"}; // unfunded account we want to create BridgeDef xrp_b{ doorA, @@ -2309,8 +2317,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj auto const amt = XRP(777); auto const amt_plus_reward = amt + xrp_b.reward; { - Balance bal_doorA(mcEnv, doorA); - Balance bal_a(mcEnv, a); + Balance const bal_doorA(mcEnv, doorA); + Balance const bal_a(mcEnv, a); mcEnv.tx(sidechain_xchain_account_create(a, xrp_b.jvb, ua, amt, xrp_b.reward)).close(); @@ -2336,9 +2344,13 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj scEnv.tx(att, ter(expectedTER)).close(); if (i + 1 < xrp_b.quorum) + { BEAST_EXPECT(!scEnv.env_.le(ua)); + } else + { BEAST_EXPECT(scEnv.env_.le(ua)); + } } BEAST_EXPECT(scEnv.env_.le(ua)); } @@ -2348,8 +2360,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj { using namespace jtx; - XRPAmount res0 = reserve(0); - XRPAmount tx_fee = txFee(); + XRPAmount const res0 = reserve(0); + XRPAmount const tx_fee = txFee(); testcase("Claim"); @@ -2424,7 +2436,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj BalanceTransfer transfer( scEnv, Account::master, scBob, scAlice, &payees[0], 1, withClaim); - jtx::signer master_signer(Account::master); + jtx::signer const master_signer(Account::master); scEnv .tx(claim_attestation( scAttester, @@ -2469,7 +2481,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj BalanceTransfer transfer( scEnv, Account::master, scBob, scAlice, &payees[0], 1, withClaim); - jtx::signer master_signer(payees[0]); + jtx::signer const master_signer(payees[0]); scEnv .tx(claim_attestation( scAttester, @@ -2849,7 +2861,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj XEnv scEnv(*this, true); mcEnv.tx(create_bridge(mcDoor, jvb)).close(); - STAmount huge_reward{XRP(20000)}; + STAmount const huge_reward{XRP(20000)}; BEAST_EXPECT(huge_reward > scEnv.balance(scAlice)); scEnv.tx(create_bridge(Account::master, jvb, huge_reward)) @@ -2997,7 +3009,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // the transfer failed, but check that we can still use the // claimID with a different account - Balance scCarol_bal(scEnv, scCarol); + Balance const scCarol_bal(scEnv, scCarol); scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol)).close(); BEAST_EXPECT(scCarol_bal.diff() == amt); @@ -3014,7 +3026,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .tx(fset("scBob", 0, asfDepositAuth)) // clear deposit auth .close(); - Balance scBob_bal(scEnv, scBob); + Balance const scBob_bal(scEnv, scBob); scEnv.tx(txns.back()).close(); BEAST_EXPECT(scBob_bal.diff() == amt); } @@ -3066,7 +3078,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // the transfer failed, but check that we can still use the // claimID with a different account - Balance scCarol_bal(scEnv, scCarol); + Balance const scCarol_bal(scEnv, scCarol); scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol)).close(); BEAST_EXPECT(scCarol_bal.diff() == amt); @@ -3083,7 +3095,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .tx(fset("scBob", 0, asfRequireDest)) // clear dest tag .close(); - Balance scBob_bal(scEnv, scBob); + Balance const scBob_bal(scEnv, scBob); scEnv.tx(txns.back()).close(); BEAST_EXPECT(scBob_bal.diff() == amt); @@ -3114,7 +3126,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // we should be able to submit the attestations, but the transfer // should not occur because dest account has deposit auth set - Balance scBob_bal(scEnv, scBob); + Balance const scBob_bal(scEnv, scBob); scEnv.multiTx(claim_attestations( scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)); @@ -3122,7 +3134,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // Check that check that we still can use the claimID to transfer // the amount to a different account - Balance scCarol_bal(scEnv, scCarol); + Balance const scCarol_bal(scEnv, scCarol); scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol)).close(); BEAST_EXPECT(scCarol_bal.diff() == amt); @@ -3201,7 +3213,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim); - Balance scAlice_bal(scEnv, scAlice); + Balance const scAlice_bal(scEnv, scAlice); scEnv.multiTx(claim_attestations( scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)); @@ -3249,7 +3261,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim); - Balance scAlice_bal(scEnv, scAlice); + Balance const scAlice_bal(scEnv, scAlice); scEnv.multiTx(claim_attestations( scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)); STAmount claim_cost = tiny_reward; @@ -3338,7 +3350,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); // balance of last signer should not change (has deposit auth) - Balance last_signer(scEnv, unpaid); + Balance const last_signer(scEnv, unpaid); // make sure all signers except the last one get the // split_reward @@ -3402,7 +3414,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj using namespace jtx; testcase("Bridge Create Account"); - XRPAmount tx_fee = txFee(); + XRPAmount const tx_fee = txFee(); // coverage test: transferHelper() - dst == src { @@ -3415,7 +3427,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .tx(jtx::signers(Account::master, quorum, signers)) .close(); - Balance door(scEnv, Account::master); + Balance const door(scEnv, Account::master); // scEnv.tx(att_create_acct_batch1(1, amt, // Account::master)).close(); @@ -3439,8 +3451,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close(); - Balance door(mcEnv, mcDoor); - Balance carol(mcEnv, mcCarol); + Balance const door(mcEnv, mcDoor); + Balance const carol(mcEnv, mcCarol); mcEnv .tx(sidechain_xchain_account_create(mcCarol, jvb, scuAlice, XRP(19), reward), @@ -3457,7 +3469,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close(); - Balance door(mcEnv, mcDoor); + Balance const door(mcEnv, mcDoor); mcEnv .tx(sidechain_xchain_account_create(mcCarol, jvb, scuAlice, XRP(20), reward), @@ -3475,7 +3487,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close(); - Balance door(mcEnv, mcDoor); + Balance const door(mcEnv, mcDoor); mcEnv.disableFeature(featureXChainBridge) .tx(sidechain_xchain_account_create(mcCarol, jvb, scuAlice, XRP(20), reward), @@ -3491,7 +3503,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close(); - Balance door(mcEnv, mcDoor); + Balance const door(mcEnv, mcDoor); mcEnv .tx(sidechain_xchain_account_create(mcCarol, jvb, scuAlice, XRP(-20), reward), @@ -3507,7 +3519,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close(); - Balance door(mcEnv, mcDoor); + Balance const door(mcEnv, mcDoor); mcEnv .tx(sidechain_xchain_account_create(mcCarol, jvb, scuAlice, XRP(20), XRP(-1)), @@ -3523,7 +3535,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close(); - Balance door(mcEnv, mcDoor); + Balance const door(mcEnv, mcDoor); mcEnv .tx(sidechain_xchain_account_create(mcDoor, jvb, scuAlice, XRP(20), XRP(1)), @@ -3539,7 +3551,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close(); - Balance door(mcEnv, mcDoor); + Balance const door(mcEnv, mcDoor); mcEnv .tx(sidechain_xchain_account_create(mcCarol, jvb, scuAlice, XRP(20), XRP(2)), @@ -3554,8 +3566,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj testFeeDipsIntoReserve() { using namespace jtx; - XRPAmount res0 = reserve(0); - XRPAmount tx_fee = txFee(); + XRPAmount const res0 = reserve(0); + XRPAmount const tx_fee = txFee(); testcase("Fee dips into reserve"); @@ -3648,7 +3660,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // Create a bridge and add an attestation with a bad public key XEnv scEnv(*this, true); std::uint32_t const claimID = 1; - std::optional dst{scBob}; + std::optional const dst{scBob}; auto const amt = XRP(1000); scEnv.tx(create_bridge(Account::master, jvb)) .tx(jtx::signers(Account::master, quorum, signers)) @@ -3677,7 +3689,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // public key XEnv scEnv(*this, true); std::uint32_t const createCount = 1; - Account dst{scBob}; + Account const dst{scBob}; auto const amt = XRP(1000); auto const rewardAmt = XRP(1); scEnv.tx(create_bridge(Account::master, jvb)) @@ -3777,8 +3789,8 @@ private: bool verify(ENV& env, jtx::Account const& acct) const { - STAmount diff{env.balance(acct) - startAmount}; - bool check = diff == expectedDiff; + STAmount const diff{env.balance(acct) - startAmount}; + bool const check = diff == expectedDiff; return check; } }; @@ -3826,7 +3838,7 @@ private: void sendAttestations() { - bool callback_called; + bool callback_called = false; // we have this "do {} while" loop because we want to process // all the account create which can reach quorum at this time @@ -3844,7 +3856,7 @@ private: auto& c = counters[bridge]; auto& create_claims = claims.create_claims[c.claim_count]; auto num_attns = create_claims.size(); - if (num_attns) + if (num_attns != 0u) { c.num_create_attn_sent += sendCreateAttestations(i, bridge, create_claims); @@ -3917,8 +3929,10 @@ private: verify() const { for (auto const& [acct, state] : accounts) + { if (!state.verify(env, acct)) return false; + } return true; } @@ -3997,12 +4011,12 @@ private: template class SmBase { - public: SmBase(std::shared_ptr const& chainstate, BridgeDef const& bridge) : bridge_(bridge), st_(chainstate) { } + public: ChainStateTrack& srcState() { @@ -4030,6 +4044,8 @@ private: protected: BridgeDef const& bridge_; std::shared_ptr st_; + + friend T; }; // -------------------------------------------------- @@ -4042,7 +4058,7 @@ private: std::shared_ptr const& chainstate, BridgeDef const& bridge, AccountCreate create) - : Base(chainstate, bridge), sm_state(st_initial), cr(std::move(create)) + : Base(chainstate, bridge), cr(std::move(create)) { } @@ -4074,10 +4090,10 @@ private: ChainStateTrack& st = destState(); // check all signers, but start at a random one - size_t i; + size_t i = 0; for (i = 0; i < num_signers; ++i) { - size_t signer_idx = (rnd + i) % num_signers; + size_t const signer_idx = (rnd + i) % num_signers; if (!(cr.attested[signer_idx])) { @@ -4155,7 +4171,7 @@ private: } private: - SmState sm_state; + SmState sm_state{st_initial}; AccountCreate cr; }; @@ -4169,7 +4185,7 @@ private: std::shared_ptr const& chainstate, BridgeDef const& bridge, Transfer xfer) - : Base(chainstate, bridge), xfer(std::move(xfer)), sm_state(st_initial) + : Base(chainstate, bridge), xfer(std::move(xfer)) { } @@ -4235,7 +4251,7 @@ private: // check all signers, but start at a random one for (size_t i = 0; i < num_signers; ++i) { - size_t signer_idx = (rnd + i) % num_signers; + size_t const signer_idx = (rnd + i) % num_signers; if (!(xfer.attested[signer_idx])) { // enqueue one attestation for this signer @@ -4259,7 +4275,7 @@ private: } // return true if quorum was reached, false otherwise - bool quorum = + bool const quorum = std::count(xfer.attested.begin(), xfer.attested.end(), true) >= bridge_.quorum; if (quorum && xfer.with_claim == WithClaim::no) { @@ -4295,9 +4311,14 @@ private: break; case st_attesting: - sm_state = attest(time, rnd) - ? (xfer.with_claim == WithClaim::yes ? st_attested : st_completed) - : st_attesting; + if (attest(time, rnd)) + { + sm_state = xfer.with_claim == WithClaim::yes ? st_attested : st_completed; + } + else + { + sm_state = st_attesting; + } break; case st_attested: @@ -4316,7 +4337,7 @@ private: private: Transfer xfer; - SmState sm_state; + SmState sm_state{st_initial}; }; // -------------------------------------------------- @@ -4359,14 +4380,18 @@ public: for (auto it = sm_.begin(); it != sm_.end();) { auto vis = [&](auto& sm) { - uint32_t rnd = distrib(gen); + uint32_t const rnd = distrib(gen); return sm.advance(time, rnd); }; auto& [t, sm] = *it; if (t <= time && std::visit(vis, sm) == st_completed) + { it = sm_.erase(it); + } else + { ++it; + } } // send attestations @@ -4406,8 +4431,10 @@ public: std::vector result; result.reserve(num_acct); for (int i = 0; i < num_acct; ++i) + { result.emplace_back( "a"s + std::to_string(i), (i % 2) ? KeyType::ed25519 : KeyType::secp256k1); + } result.emplace_back("doorXRPLocking"); doorXRPLocking = result.back(); result.emplace_back("doorUSDLocking"); @@ -4419,14 +4446,14 @@ public: for (auto& acct : a) { - STAmount amt{XRP(100000)}; + STAmount const amt{XRP(100000)}; mcEnv.fund(amt, acct); scEnv.fund(amt, acct); } - Account USDLocking{"USDLocking"}; - IOU usdLocking{USDLocking["USD"]}; - IOU usdIssuing{doorUSDIssuing["USD"]}; + Account const USDLocking{"USDLocking"}; + IOU const usdLocking{USDLocking["USD"]}; + IOU const usdIssuing{doorUSDIssuing["USD"]}; mcEnv.fund(XRP(100000), USDLocking); mcEnv.close(); @@ -4456,8 +4483,10 @@ public: std::vector result; result.reserve(num_ua); for (int i = 0; i < num_ua; ++i) + { result.emplace_back( "ua"s + std::to_string(i), (i % 2) ? KeyType::ed25519 : KeyType::secp256k1); + } return result; }(); diff --git a/src/test/app/tx/apply_test.cpp b/src/test/app/tx/apply_test.cpp index f21af50817..6d3efea5f3 100644 --- a/src/test/app/tx/apply_test.cpp +++ b/src/test/app/tx/apply_test.cpp @@ -31,13 +31,13 @@ public: "8B62E6440848314BB85996936E4F595287774684DC2AC6266024BEF"; auto ret = strUnHex(non_fully_canonical_tx); - SerialIter sitTrans(makeSlice(*ret)); + SerialIter sitTrans(makeSlice(*ret)); // NOLINT(bugprone-unchecked-optional-access) STTx const tx = *std::make_shared(std::ref(sitTrans)); { test::jtx::Env fully_canonical(*this, test::jtx::testable_amendments()); - Validity valid = + Validity const valid = checkValidity( fully_canonical.app().getHashRouter(), tx, fully_canonical.current()->rules()) .first; diff --git a/src/test/basics/Buffer_test.cpp b/src/test/basics/Buffer_test.cpp index 56f440b993..95a8853975 100644 --- a/src/test/basics/Buffer_test.cpp +++ b/src/test/basics/Buffer_test.cpp @@ -9,10 +9,10 @@ namespace test { struct Buffer_test : beast::unit_test::suite { - bool - sane(Buffer const& b) const + static bool + sane(Buffer const& b) { - if (b.size() == 0) + if (b.empty()) return b.data() == nullptr; return b.data() != nullptr; @@ -26,7 +26,7 @@ struct Buffer_test : beast::unit_test::suite 0xac, 0x2d, 0x89, 0x4d, 0x19, 0x9c, 0xf0, 0x2c, 0x15, 0xd1, 0xf9, 0x9b, 0x66, 0xd2, 0x30, 0xd3}; - Buffer b0; + Buffer const b0; BEAST_EXPECT(sane(b0)); BEAST_EXPECT(b0.empty()); @@ -105,19 +105,19 @@ struct Buffer_test : beast::unit_test::suite { // Move-construct from empty buf Buffer x; - Buffer y{std::move(x)}; - BEAST_EXPECT(sane(x)); - BEAST_EXPECT(x.empty()); + Buffer const y{std::move(x)}; + BEAST_EXPECT(sane(x)); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(x.empty()); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(sane(y)); BEAST_EXPECT(y.empty()); - BEAST_EXPECT(x == y); + BEAST_EXPECT(x == y); // NOLINT(bugprone-use-after-move) } { // Move-construct from non-empty buf Buffer x{b1}; - Buffer y{std::move(x)}; - BEAST_EXPECT(sane(x)); - BEAST_EXPECT(x.empty()); + Buffer const y{std::move(x)}; + BEAST_EXPECT(sane(x)); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(x.empty()); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(sane(y)); BEAST_EXPECT(y == b1); } @@ -129,8 +129,8 @@ struct Buffer_test : beast::unit_test::suite x = std::move(y); BEAST_EXPECT(sane(x)); BEAST_EXPECT(x.empty()); - BEAST_EXPECT(sane(y)); - BEAST_EXPECT(y.empty()); + BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move) } { // Move assign non-empty buf to empty buf @@ -140,8 +140,8 @@ struct Buffer_test : beast::unit_test::suite x = std::move(y); BEAST_EXPECT(sane(x)); BEAST_EXPECT(x == b1); - BEAST_EXPECT(sane(y)); - BEAST_EXPECT(y.empty()); + BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move) } { // Move assign empty buf to non-empty buf @@ -151,8 +151,8 @@ struct Buffer_test : beast::unit_test::suite x = std::move(y); BEAST_EXPECT(sane(x)); BEAST_EXPECT(x.empty()); - BEAST_EXPECT(sane(y)); - BEAST_EXPECT(y.empty()); + BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move) } { // Move assign non-empty buf to non-empty buf @@ -163,14 +163,14 @@ struct Buffer_test : beast::unit_test::suite x = std::move(y); BEAST_EXPECT(sane(x)); BEAST_EXPECT(!x.empty()); - BEAST_EXPECT(sane(y)); - BEAST_EXPECT(y.empty()); + BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move) x = std::move(z); BEAST_EXPECT(sane(x)); BEAST_EXPECT(!x.empty()); - BEAST_EXPECT(sane(z)); - BEAST_EXPECT(z.empty()); + BEAST_EXPECT(sane(z)); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(z.empty()); // NOLINT(bugprone-use-after-move) } } @@ -241,13 +241,13 @@ struct Buffer_test : beast::unit_test::suite // Try to clear: x.clear(); BEAST_EXPECT(sane(x)); - BEAST_EXPECT(x.size() == 0); + BEAST_EXPECT(x.empty()); BEAST_EXPECT(x.data() == nullptr); // Try to clear again: x.clear(); BEAST_EXPECT(sane(x)); - BEAST_EXPECT(x.size() == 0); + BEAST_EXPECT(x.empty()); BEAST_EXPECT(x.data() == nullptr); }; diff --git a/src/test/basics/Expected_test.cpp b/src/test/basics/Expected_test.cpp index 2caa15816f..fa35946624 100644 --- a/src/test/basics/Expected_test.cpp +++ b/src/test/basics/Expected_test.cpp @@ -46,7 +46,7 @@ struct Expected_test : beast::unit_test::suite BEAST_EXPECT(expected.value() == "Valid value"); BEAST_EXPECT(*expected == "Valid value"); BEAST_EXPECT(expected->at(0) == 'V'); - std::string mv = std::move(*expected); + std::string const mv = std::move(*expected); BEAST_EXPECT(mv == "Valid value"); bool throwOccurred = false; diff --git a/src/test/basics/FileUtilities_test.cpp b/src/test/basics/FileUtilities_test.cpp index 3d6f9b754b..b63f8baf0a 100644 --- a/src/test/basics/FileUtilities_test.cpp +++ b/src/test/basics/FileUtilities_test.cpp @@ -17,7 +17,7 @@ public: constexpr char const* expectedContents = "This file is very short. That's all we need."; - FileDirGuard file( + FileDirGuard const file( *this, "test_file", "test.txt", "This is temporary text that should get overwritten"); error_code ec; diff --git a/src/test/basics/IOUAmount_test.cpp b/src/test/basics/IOUAmount_test.cpp index 73ab0343cd..d0e272c28b 100644 --- a/src/test/basics/IOUAmount_test.cpp +++ b/src/test/basics/IOUAmount_test.cpp @@ -55,7 +55,7 @@ public: using beast::zero; { - IOUAmount z(zero); + IOUAmount const z(zero); BEAST_EXPECT(z == zero); BEAST_EXPECT(z >= zero); BEAST_EXPECT(z <= zero); @@ -94,9 +94,11 @@ public: BEAST_EXPECT(z >= z); BEAST_EXPECT(z <= z); BEAST_EXPECT(z == -z); + // NOLINTBEGIN(misc-redundant-expression) unexpected(z > z); unexpected(z < z); unexpected(z != z); + // NOLINTEND(misc-redundant-expression) unexpected(z != -z); BEAST_EXPECT(n < z); @@ -150,7 +152,7 @@ public: for (auto const mantissaSize : {MantissaRange::small, MantissaRange::large}) { - NumberMantissaScaleGuard mg(mantissaSize); + NumberMantissaScaleGuard const mg(mantissaSize); test(IOUAmount(-2, 0), "-2"); test(IOUAmount(0, 0), "0"); @@ -181,14 +183,14 @@ public: { // multiply by a number that would overflow the mantissa, then // divide by the same number, and check we didn't lose any value - IOUAmount bigMan(maxMantissa, 0); + IOUAmount const bigMan(maxMantissa, 0); BEAST_EXPECT(bigMan == mulRatio(bigMan, maxUInt, maxUInt, true)); // rounding mode shouldn't matter as the result is exact BEAST_EXPECT(bigMan == mulRatio(bigMan, maxUInt, maxUInt, false)); } { // Similar test as above, but for negative values - IOUAmount bigMan(-maxMantissa, 0); + IOUAmount const bigMan(-maxMantissa, 0); BEAST_EXPECT(bigMan == mulRatio(bigMan, maxUInt, maxUInt, true)); // rounding mode shouldn't matter as the result is exact BEAST_EXPECT(bigMan == mulRatio(bigMan, maxUInt, maxUInt, false)); @@ -196,7 +198,7 @@ public: { // small amounts - IOUAmount tiny(minMantissa, minExponent); + IOUAmount const tiny(minMantissa, minExponent); // Round up should give the smallest allowable number BEAST_EXPECT(tiny == mulRatio(tiny, 1, maxUInt, true)); BEAST_EXPECT(tiny == mulRatio(tiny, maxUInt - 1, maxUInt, true)); @@ -205,7 +207,7 @@ public: BEAST_EXPECT(beast::zero == mulRatio(tiny, maxUInt - 1, maxUInt, false)); // tiny negative numbers - IOUAmount tinyNeg(-minMantissa, minExponent); + IOUAmount const tinyNeg(-minMantissa, minExponent); // Round up should give zero BEAST_EXPECT(beast::zero == mulRatio(tinyNeg, 1, maxUInt, true)); BEAST_EXPECT(beast::zero == mulRatio(tinyNeg, maxUInt - 1, maxUInt, true)); @@ -216,20 +218,20 @@ public: { // rounding { - IOUAmount one(1, 0); + IOUAmount const one(1, 0); auto const rup = mulRatio(one, maxUInt - 1, maxUInt, true); auto const rdown = mulRatio(one, maxUInt - 1, maxUInt, false); BEAST_EXPECT(rup.mantissa() - rdown.mantissa() == 1); } { - IOUAmount big(maxMantissa, maxExponent); + IOUAmount const big(maxMantissa, maxExponent); auto const rup = mulRatio(big, maxUInt - 1, maxUInt, true); auto const rdown = mulRatio(big, maxUInt - 1, maxUInt, false); BEAST_EXPECT(rup.mantissa() - rdown.mantissa() == 1); } { - IOUAmount negOne(-1, 0); + IOUAmount const negOne(-1, 0); auto const rup = mulRatio(negOne, maxUInt - 1, maxUInt, true); auto const rdown = mulRatio(negOne, maxUInt - 1, maxUInt, false); BEAST_EXPECT(rup.mantissa() - rdown.mantissa() == 1); diff --git a/src/test/basics/IntrusiveShared_test.cpp b/src/test/basics/IntrusiveShared_test.cpp index 817cbb8c1d..52cb8f5c1c 100644 --- a/src/test/basics/IntrusiveShared_test.cpp +++ b/src/test/basics/IntrusiveShared_test.cpp @@ -144,7 +144,7 @@ public: } void - partialDestructor() + partialDestructor() const { using enum TrackedState; @@ -191,16 +191,16 @@ public: testcase("Basics"); { - TIBase::ResetStatesGuard rsg{true}; + TIBase::ResetStatesGuard const rsg{true}; - TIBase b; + TIBase const b; BEAST_EXPECT(b.use_count() == 1); b.addWeakRef(); BEAST_EXPECT(b.use_count() == 1); auto s = b.releaseStrongRef(); BEAST_EXPECT(s == ReleaseStrongRefAction::partialDestroy); BEAST_EXPECT(b.use_count() == 0); - TIBase* pb = &b; + TIBase const* pb = &b; partialDestructorFinished(&pb); BEAST_EXPECT(!pb); auto w = b.releaseWeakRef(); @@ -210,7 +210,7 @@ public: std::vector> strong; std::vector> weak; { - TIBase::ResetStatesGuard rsg{true}; + TIBase::ResetStatesGuard const rsg{true}; using enum TrackedState; auto b = make_SharedIntrusive(); @@ -245,13 +245,13 @@ public: while (!weak.empty()) { weak.resize(weak.size() - 1); - if (weak.size()) + if (!weak.empty()) BEAST_EXPECT(TIBase::getState(id) == partiallyDeleted); } BEAST_EXPECT(TIBase::getState(id) == deleted); } { - TIBase::ResetStatesGuard rsg{true}; + TIBase::ResetStatesGuard const rsg{true}; using enum TrackedState; auto b = make_SharedIntrusive(); @@ -275,7 +275,7 @@ public: BEAST_EXPECT(TIBase::getState(id) == deleted); } { - TIBase::ResetStatesGuard rsg{true}; + TIBase::ResetStatesGuard const rsg{true}; using enum TrackedState; using swu = SharedWeakUnion; @@ -309,7 +309,7 @@ public: { // Testing SharedWeakUnion assignment operator - TIBase::ResetStatesGuard rsg{true}; + TIBase::ResetStatesGuard const rsg{true}; auto strong1 = make_SharedIntrusive(); auto strong2 = make_SharedIntrusive(); @@ -338,7 +338,7 @@ public: // 2) Test self-assignment BEAST_EXPECT(union1.isStrong()); BEAST_EXPECT(TIBase::getState(id1) == TrackedState::alive); - int initialRefCount = strong1->use_count(); + int const initialRefCount = strong1->use_count(); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wself-assign-overloaded" union1 = union1; // Self-assignment @@ -374,7 +374,7 @@ public: using enum TrackedState; - TIBase::ResetStatesGuard rsg{true}; + TIBase::ResetStatesGuard const rsg{true}; auto strong = make_SharedIntrusive(); WeakIntrusive weak{strong}; @@ -441,7 +441,7 @@ public: using enum TrackedState; - TIBase::ResetStatesGuard rsg{true}; + TIBase::ResetStatesGuard const rsg{true}; auto strong = make_SharedIntrusive(); WeakIntrusive weak{strong}; @@ -486,12 +486,12 @@ public: // and check that the invariants hold. using enum TrackedState; - TIBase::ResetStatesGuard rsg{true}; + TIBase::ResetStatesGuard const rsg{true}; std::atomic destructionState{0}; // returns destructorRan and partialDestructorRan (in that order) auto getDestructorState = [&]() -> std::pair { - int s = destructionState.load(std::memory_order_relaxed); + int const s = destructionState.load(std::memory_order_relaxed); return {(s & 1) != 0, (s & 2) != 0}; }; auto setDestructorRan = [&]() -> void { @@ -592,6 +592,7 @@ public: } }; std::vector threads; + threads.reserve(numThreads); for (int i = 0; i < numThreads; ++i) { threads.emplace_back(cloneAndDestroy, i); @@ -619,12 +620,12 @@ public: using enum TrackedState; - TIBase::ResetStatesGuard rsg{true}; + TIBase::ResetStatesGuard const rsg{true}; std::atomic destructionState{0}; // returns destructorRan and partialDestructorRan (in that order) auto getDestructorState = [&]() -> std::pair { - int s = destructionState.load(std::memory_order_relaxed); + int const s = destructionState.load(std::memory_order_relaxed); return {(s & 1) != 0, (s & 2) != 0}; }; auto setDestructorRan = [&]() -> void { @@ -737,6 +738,7 @@ public: } }; std::vector threads; + threads.reserve(numThreads); for (int i = 0; i < numThreads; ++i) { threads.emplace_back(cloneAndDestroy, i); @@ -759,12 +761,12 @@ public: using enum TrackedState; - TIBase::ResetStatesGuard rsg{true}; + TIBase::ResetStatesGuard const rsg{true}; std::atomic destructionState{0}; // returns destructorRan and partialDestructorRan (in that order) auto getDestructorState = [&]() -> std::pair { - int s = destructionState.load(std::memory_order_relaxed); + int const s = destructionState.load(std::memory_order_relaxed); return {(s & 1) != 0, (s & 2) != 0}; }; auto setDestructorRan = [&]() -> void { @@ -830,7 +832,7 @@ public: // Multiple threads all create a weak pointer from the same // strong pointer - WeakIntrusive weak{toLock[threadId]}; + WeakIntrusive const weak{toLock[threadId]}; for (int wi = 0; wi < lockWeakLoopIters; ++wi) { BEAST_EXPECT(!weak.expired()); @@ -845,6 +847,7 @@ public: } }; std::vector threads; + threads.reserve(numThreads); for (int i = 0; i < numThreads; ++i) { threads.emplace_back(lockAndDestroy, i); diff --git a/src/test/basics/Number_test.cpp b/src/test/basics/Number_test.cpp index eaf184ffe4..856b379533 100644 --- a/src/test/basics/Number_test.cpp +++ b/src/test/basics/Number_test.cpp @@ -37,7 +37,8 @@ public: auto const minMantissa = Number::minMantissa(); try { - Number x = Number{false, minMantissa * 10, 32768, Number::normalized{}}; + [[maybe_unused]] Number const x = + Number{false, minMantissa * 10, 32768, Number::normalized{}}; } catch (std::overflow_error const&) { @@ -86,7 +87,7 @@ public: try { [[maybe_unused]] - Number q = Number{false, minMantissa, 32767, Number::normalized{}} * 100; + Number const q = Number{false, minMantissa, 32767, Number::normalized{}} * 100; } catch (std::overflow_error const&) { @@ -199,9 +200,13 @@ public: } }; if (scale == MantissaRange::small) + { test(cSmall); + } else + { test(cLarge); + } { bool caught = false; try @@ -275,7 +280,7 @@ public: {Number{Number::maxRep}, Number{6, -1}, Number{Number::maxRep - 1}}, {Number{false, Number::maxRep + 1, 0, Number::normalized{}}, Number{1, 0}, - Number{Number::maxRep / 10 + 1, 1}}, + Number{(Number::maxRep / 10) + 1, 1}}, {Number{false, Number::maxRep + 1, 0, Number::normalized{}}, Number{3, 0}, Number{Number::maxRep}}, @@ -291,9 +296,13 @@ public: } }; if (scale == MantissaRange::small) + { test(cSmall); + } else + { test(cLarge); + } } void @@ -314,13 +323,17 @@ public: }; auto tests = [&](auto const& cSmall, auto const& cLarge) { if (scale == MantissaRange::small) + { test(cSmall); + } else + { test(cLarge); + } }; auto const maxMantissa = Number::maxMantissa(); - saveNumberRoundMode save{Number::setround(Number::to_nearest)}; + saveNumberRoundMode const save{Number::setround(Number::to_nearest)}; { auto const cSmall = std::to_array({ {Number{7}, Number{8}, Number{56}}, @@ -444,7 +457,7 @@ public: // 99'999'999'999'999'999'800'000'000'000'000'000'100 {Number{false, maxMantissa, 0, Number::normalized{}}, Number{false, maxMantissa, 0, Number::normalized{}}, - Number{false, maxMantissa / 10 - 1, 20, Number::normalized{}}}, + Number{false, (maxMantissa / 10) - 1, 20, Number::normalized{}}}, // Maximum int64 range // 85'070'591'730'234'615'847'396'907'784'232'501'249 {Number{Number::maxRep, 0}, @@ -511,7 +524,7 @@ public: // 99'999'999'999'999'999'800'000'000'000'000'000'100 {Number{false, maxMantissa, 0, Number::normalized{}}, Number{false, maxMantissa, 0, Number::normalized{}}, - Number{false, maxMantissa / 10 - 1, 20, Number::normalized{}}}, + Number{false, (maxMantissa / 10) - 1, 20, Number::normalized{}}}, // Maximum int64 range // 85'070'591'730'234'615'847'396'907'784'232'501'249 {Number{Number::maxRep, 0}, @@ -622,11 +635,15 @@ public: auto const maxMantissa = Number::maxMantissa(); auto tests = [&](auto const& cSmall, auto const& cLarge) { if (scale == MantissaRange::small) + { test(cSmall); + } else + { test(cLarge); + } }; - saveNumberRoundMode save{Number::setround(Number::to_nearest)}; + saveNumberRoundMode const save{Number::setround(Number::to_nearest)}; { auto const cSmall = std::to_array( {{Number{1}, Number{2}, Number{5, -1}}, @@ -841,7 +858,7 @@ public: test(cSmall); if (Number::getMantissaScale() != MantissaRange::small) { - NumberRoundModeGuard mg(Number::towards_zero); + NumberRoundModeGuard const mg(Number::towards_zero); test(cLarge); } bool caught = false; @@ -912,7 +929,7 @@ public: { testcase << "test_power1 " << to_string(Number::getMantissaScale()); using Case = std::tuple; - Case c[]{ + Case const c[]{ {Number{64}, 0, Number{1}}, {Number{64}, 1, Number{64}}, {Number{64}, 2, Number{4096}}, @@ -930,7 +947,7 @@ public: { testcase << "test_power2 " << to_string(Number::getMantissaScale()); using Case = std::tuple; - Case c[]{ + Case const c[]{ {Number{1}, 3, 7, Number{1}}, {Number{-1}, 1, 0, Number{1}}, {Number{-1, -1}, 1, 0, Number{0}}, @@ -976,24 +993,24 @@ public: { testcase << "testConversions " << to_string(Number::getMantissaScale()); - IOUAmount x{5, 6}; - Number y = x; + IOUAmount const x{5, 6}; + Number const y = x; BEAST_EXPECT((y == Number{5, 6})); - IOUAmount z{y}; + IOUAmount const z{y}; BEAST_EXPECT(x == z); - XRPAmount xrp{500}; - STAmount st = xrp; - Number n = st; + XRPAmount const xrp{500}; + STAmount const st = xrp; + Number const n = st; BEAST_EXPECT(XRPAmount{n} == xrp); - IOUAmount x0{0, 0}; - Number y0 = x0; + IOUAmount const x0{0, 0}; + Number const y0 = x0; BEAST_EXPECT((y0 == Number{0})); - IOUAmount z0{y0}; + IOUAmount const z0{y0}; BEAST_EXPECT(x0 == z0); - XRPAmount xrp0{0}; - Number n0 = xrp0; + XRPAmount const xrp0{0}; + Number const n0 = xrp0; BEAST_EXPECT(n0 == Number{0}); - XRPAmount xrp1{n0}; + XRPAmount const xrp1{n0}; BEAST_EXPECT(xrp1 == xrp0); } @@ -1002,9 +1019,9 @@ public: { testcase << "test_to_integer " << to_string(Number::getMantissaScale()); using Case = std::tuple; - saveNumberRoundMode save{Number::setround(Number::to_nearest)}; + saveNumberRoundMode const save{Number::setround(Number::to_nearest)}; { - Case c[]{ + Case const c[]{ {Number{0}, 0}, {Number{1}, 1}, {Number{2}, 2}, @@ -1042,7 +1059,7 @@ public: auto prev_mode = Number::setround(Number::towards_zero); BEAST_EXPECT(prev_mode == Number::to_nearest); { - Case c[]{ + Case const c[]{ {Number{0}, 0}, {Number{1}, 1}, {Number{2}, 2}, @@ -1080,7 +1097,7 @@ public: prev_mode = Number::setround(Number::downward); BEAST_EXPECT(prev_mode == Number::towards_zero); { - Case c[]{ + Case const c[]{ {Number{0}, 0}, {Number{1}, 1}, {Number{2}, 2}, @@ -1118,7 +1135,7 @@ public: prev_mode = Number::setround(Number::upward); BEAST_EXPECT(prev_mode == Number::downward); { - Case c[]{ + Case const c[]{ {Number{0}, 0}, {Number{1}, 1}, {Number{2}, 2}, @@ -1169,7 +1186,7 @@ public: test_squelch() { testcase << "test_squelch " << to_string(Number::getMantissaScale()); - Number limit{1, -6}; + Number const limit{1, -6}; BEAST_EXPECT((squelch(Number{2, -6}, limit) == Number{2, -6})); BEAST_EXPECT((squelch(Number{1, -6}, limit) == Number{1, -6})); BEAST_EXPECT((squelch(Number{9, -7}, limit) == Number{0})); @@ -1217,15 +1234,15 @@ public: test(Number::max(), "9999999999999999e32768"); test(Number::lowest(), "-9999999999999999e32768"); { - NumberRoundModeGuard mg(Number::towards_zero); + NumberRoundModeGuard const mg(Number::towards_zero); auto const maxMantissa = Number::maxMantissa(); BEAST_EXPECT(maxMantissa == 9'999'999'999'999'999); test( - Number{false, maxMantissa * 1000 + 999, -3, Number::normalized()}, + Number{false, (maxMantissa * 1000) + 999, -3, Number::normalized()}, "9999999999999999"); test( - Number{true, maxMantissa * 1000 + 999, -3, Number::normalized()}, + Number{true, (maxMantissa * 1000) + 999, -3, Number::normalized()}, "-9999999999999999"); test(Number{std::numeric_limits::max(), -3}, "9223372036854775"); @@ -1247,7 +1264,7 @@ public: test(Number::max(), "9223372036854775807e32768"); test(Number::lowest(), "-9223372036854775807e32768"); { - NumberRoundModeGuard mg(Number::towards_zero); + NumberRoundModeGuard const mg(Number::towards_zero); auto const maxMantissa = Number::maxMantissa(); BEAST_EXPECT(maxMantissa == 9'999'999'999'999'999'999ULL); @@ -1298,7 +1315,7 @@ public: test_stream() { testcase << "test_stream " << to_string(Number::getMantissaScale()); - Number x{100}; + Number const x{100}; std::ostringstream os; os << x; BEAST_EXPECT(os.str() == to_string(x)); @@ -1309,7 +1326,7 @@ public: { testcase << "test_inc_dec " << to_string(Number::getMantissaScale()); Number x{100}; - Number y = +x; + Number const y = +x; BEAST_EXPECT(x == y); BEAST_EXPECT(x++ == y); BEAST_EXPECT(x == Number{101}); @@ -1320,7 +1337,7 @@ public: void test_toSTAmount() { - NumberSO stNumberSO{true}; + NumberSO const stNumberSO{true}; Issue const issue; Number const n{7'518'783'80596, -5}; saveNumberRoundMode const save{Number::setround(Number::to_nearest)}; @@ -1462,7 +1479,7 @@ public: { for (auto const& [mode, val] : roundings) { - NumberRoundModeGuard g{mode}; + NumberRoundModeGuard const g{mode}; auto const res = static_cast(num); BEAST_EXPECTS( res == val, @@ -1480,7 +1497,7 @@ public: // Control case BEAST_EXPECT(Number::maxMantissa() > 10); - Number ten{10}; + Number const ten{10}; BEAST_EXPECT(ten.exponent() <= 0); if (scale == MantissaRange::small) @@ -1512,7 +1529,7 @@ public: // 85'070'591'730'234'615'847'396'907'784'232'501'249 - 38 digits BEAST_EXPECT((power(maxInt64, 2) == Number{85'070'591'730'234'615'85, 19})); - NumberRoundModeGuard mg(Number::towards_zero); + NumberRoundModeGuard const mg(Number::towards_zero); auto const maxMantissa = Number::maxMantissa(); Number const max = Number{false, maxMantissa, 0, Number::normalized{}}; @@ -1521,7 +1538,7 @@ public: // 99'999'999'999'999'999'800'000'000'000'000'000'100 - also 38 // digits BEAST_EXPECT( - (power(max, 2) == Number{false, maxMantissa / 10 - 1, 20, Number::normalized{}})); + (power(max, 2) == Number{false, (maxMantissa / 10) - 1, 20, Number::normalized{}})); } } @@ -1530,7 +1547,7 @@ public: { for (auto const scale : {MantissaRange::small, MantissaRange::large}) { - NumberMantissaScaleGuard sg(scale); + NumberMantissaScaleGuard const sg(scale); testZero(); test_limits(); testToString(); diff --git a/src/test/basics/PerfLog_test.cpp b/src/test/basics/PerfLog_test.cpp index a2976301ef..470a52d220 100644 --- a/src/test/basics/PerfLog_test.cpp +++ b/src/test/basics/PerfLog_test.cpp @@ -29,7 +29,7 @@ class PerfLog_test : public beast::unit_test::suite // We're only using Env for its Journal. That Journal gives better // coverage in unit tests. test::jtx::Env env_{*this, test::jtx::envconfig(), nullptr, beast::severities::kDisabled}; - beast::Journal j_{env_.app().journal("PerfLog_test")}; + beast::Journal j_{env_.app().getJournal("PerfLog_test")}; struct Fixture { @@ -63,21 +63,21 @@ class PerfLog_test : public beast::unit_test::suite stopSignaled = true; } - path - logDir() const + static path + logDir() { using namespace boost::filesystem; return temp_directory_path() / "perf_log_test_dir"; } - path - logFile() const + static path + logFile() { return logDir() / "perf_log.txt"; } - std::chrono::milliseconds - logInterval() const + static std::chrono::milliseconds + logInterval() { return std::chrono::milliseconds{10}; } @@ -87,14 +87,17 @@ class PerfLog_test : public beast::unit_test::suite { perf::PerfLog::Setup const setup{ withFile == WithFile::no ? "" : logFile(), logInterval()}; - return perf::make_PerfLog(setup, app_, j_, [this]() { return signalStop(); }); + return perf::make_PerfLog(setup, app_, j_, [this]() { + signalStop(); + return; + }); } // Block until the log file has grown in size, indicating that the // PerfLog has written new values to the file and _should_ have the // latest update. - void - wait() const + static void + wait() { using namespace boost::filesystem; @@ -451,8 +454,10 @@ public: Json::Value parsedLastLine; Json::Reader().parse(lastLine, parsedLastLine); if (!BEAST_EXPECT(!RPC::contains_error(parsedLastLine))) + { // Avoid cascade of failures return; + } // Validate the contents of the last line of the log. validateFinalCounters(parsedLastLine[jss::counters]); @@ -770,8 +775,10 @@ public: Json::Value parsedLastLine; Json::Reader().parse(lastLine, parsedLastLine); if (!BEAST_EXPECT(!RPC::contains_error(parsedLastLine))) + { // Avoid cascade of failures return; + } // Validate the contents of the last line of the log. validateFinalCounters(parsedLastLine[jss::counters]); @@ -793,7 +800,7 @@ public: perfLog->start(); // Randomly select a job type and its name. - JobType jobType; + JobType jobType = jtINVALID; std::string jobTypeName; { auto const& jobTypes = JobTypes::instance(); @@ -908,8 +915,10 @@ public: Json::Value parsedLastLine; Json::Reader().parse(lastLine, parsedLastLine); if (!BEAST_EXPECT(!RPC::contains_error(parsedLastLine))) + { // Avoid cascade of failures return; + } // Validate the contents of the last line of the log. verifyCounters(parsedLastLine[jss::counters], 2, 2, 24, 36); diff --git a/src/test/basics/StringUtilities_test.cpp b/src/test/basics/StringUtilities_test.cpp index 65134da8e5..fb7cdb3d69 100644 --- a/src/test/basics/StringUtilities_test.cpp +++ b/src/test/basics/StringUtilities_test.cpp @@ -13,6 +13,8 @@ public: { auto rv = strUnHex(strIn); BEAST_EXPECT(rv); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(makeSlice(*rv) == makeSlice(strExpected)); } @@ -92,7 +94,7 @@ public: BEAST_EXPECT(pUrl.username.empty()); BEAST_EXPECT(pUrl.password.empty()); BEAST_EXPECT(pUrl.domain == "domain"); - BEAST_EXPECT(*pUrl.port == 234); + BEAST_EXPECT(*pUrl.port == 234); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(pUrl.path == "/"); } @@ -114,7 +116,7 @@ public: BEAST_EXPECT(pUrl.username.empty()); BEAST_EXPECT(pUrl.password.empty()); BEAST_EXPECT(pUrl.domain == "::1"); - BEAST_EXPECT(*pUrl.port == 123); + BEAST_EXPECT(*pUrl.port == 123); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(pUrl.path == "/path"); } @@ -125,7 +127,7 @@ public: BEAST_EXPECT(pUrl.username == "user"); BEAST_EXPECT(pUrl.password == "pass"); BEAST_EXPECT(pUrl.domain == "domain"); - BEAST_EXPECT(*pUrl.port == 123); + BEAST_EXPECT(*pUrl.port == 123); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(pUrl.path == "/abc:321"); } @@ -136,7 +138,7 @@ public: BEAST_EXPECT(pUrl.username == "user"); BEAST_EXPECT(pUrl.password.empty()); BEAST_EXPECT(pUrl.domain == "domain"); - BEAST_EXPECT(*pUrl.port == 123); + BEAST_EXPECT(*pUrl.port == 123); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(pUrl.path == "/abc:321"); } @@ -147,7 +149,7 @@ public: BEAST_EXPECT(pUrl.username.empty()); BEAST_EXPECT(pUrl.password == "pass"); BEAST_EXPECT(pUrl.domain == "domain"); - BEAST_EXPECT(*pUrl.port == 123); + BEAST_EXPECT(*pUrl.port == 123); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(pUrl.path == "/abc:321"); } @@ -158,7 +160,7 @@ public: BEAST_EXPECT(pUrl.username.empty()); BEAST_EXPECT(pUrl.password.empty()); BEAST_EXPECT(pUrl.domain == "domain"); - BEAST_EXPECT(*pUrl.port == 123); + BEAST_EXPECT(*pUrl.port == 123); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(pUrl.path == "/abc:321"); } @@ -277,7 +279,7 @@ public: } { - std::string strUrl("s://" + std::string(8192, ':')); + std::string const strUrl("s://" + std::string(8192, ':')); parsedURL pUrl; BEAST_EXPECT(!parseUrl(pUrl, strUrl)); } diff --git a/src/test/basics/Units_test.cpp b/src/test/basics/Units_test.cpp index 9712ec91c0..6bb7f400cc 100644 --- a/src/test/basics/Units_test.cpp +++ b/src/test/basics/Units_test.cpp @@ -14,7 +14,7 @@ private: using FeeLevel32 = FeeLevel; { - XRPAmount x{100}; + XRPAmount const x{100}; BEAST_EXPECT(x.drops() == 100); BEAST_EXPECT((std::is_same_v)); auto y = 4u * x; @@ -25,13 +25,13 @@ private: BEAST_EXPECT(z.value() == 1600); BEAST_EXPECT((std::is_same_v)); - FeeLevel32 f{10}; - FeeLevel32 baseFee{100}; + FeeLevel32 const f{10}; + FeeLevel32 const baseFee{100}; auto drops = mulDiv(baseFee, x, f); BEAST_EXPECT(drops); - BEAST_EXPECT(drops.value() == 1000); + BEAST_EXPECT(drops.value() == 1000); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT((std::is_same_v< std::remove_reference_t::unit_type, unit::dropTag>)); @@ -39,41 +39,41 @@ private: BEAST_EXPECT((std::is_same_v, XRPAmount>)); } { - XRPAmount x{100}; + XRPAmount const x{100}; BEAST_EXPECT(x.value() == 100); BEAST_EXPECT((std::is_same_v)); auto y = 4u * x; BEAST_EXPECT(y.value() == 400); BEAST_EXPECT((std::is_same_v)); - FeeLevel64 f{10}; - FeeLevel64 baseFee{100}; + FeeLevel64 const f{10}; + FeeLevel64 const baseFee{100}; auto drops = mulDiv(baseFee, x, f); BEAST_EXPECT(drops); - BEAST_EXPECT(drops.value() == 1000); + BEAST_EXPECT(drops.value() == 1000); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT((std::is_same_v< std::remove_reference_t::unit_type, unit::dropTag>)); BEAST_EXPECT((std::is_same_v, XRPAmount>)); } { - FeeLevel64 x{1024}; + FeeLevel64 const x{1024}; BEAST_EXPECT(x.value() == 1024); BEAST_EXPECT((std::is_same_v)); - std::uint64_t m = 4; + std::uint64_t const m = 4; auto y = m * x; BEAST_EXPECT(y.value() == 4096); BEAST_EXPECT((std::is_same_v)); - XRPAmount basefee{10}; - FeeLevel64 referencefee{256}; + XRPAmount const basefee{10}; + FeeLevel64 const referencefee{256}; auto drops = mulDiv(x, basefee, referencefee); BEAST_EXPECT(drops); - BEAST_EXPECT(drops.value() == 40); + BEAST_EXPECT(drops.value() == 40); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT((std::is_same_v< std::remove_reference_t::unit_type, unit::dropTag>)); @@ -88,56 +88,56 @@ private: using FeeLevel32 = FeeLevel; { - FeeLevel32 x{std::numeric_limits::max()}; + FeeLevel32 const x{std::numeric_limits::max()}; auto y = x.jsonClipped(); BEAST_EXPECT(y.type() == Json::uintValue); BEAST_EXPECT(y == Json::Value{x.fee()}); } { - FeeLevel32 x{std::numeric_limits::min()}; + FeeLevel32 const x{std::numeric_limits::min()}; auto y = x.jsonClipped(); BEAST_EXPECT(y.type() == Json::uintValue); BEAST_EXPECT(y == Json::Value{x.fee()}); } { - FeeLevel64 x{std::numeric_limits::max()}; + FeeLevel64 const x{std::numeric_limits::max()}; auto y = x.jsonClipped(); BEAST_EXPECT(y.type() == Json::uintValue); BEAST_EXPECT(y == Json::Value{std::numeric_limits::max()}); } { - FeeLevel64 x{std::numeric_limits::min()}; + FeeLevel64 const x{std::numeric_limits::min()}; auto y = x.jsonClipped(); BEAST_EXPECT(y.type() == Json::uintValue); BEAST_EXPECT(y == Json::Value{0}); } { - FeeLevelDouble x{std::numeric_limits::max()}; + FeeLevelDouble const x{std::numeric_limits::max()}; auto y = x.jsonClipped(); BEAST_EXPECT(y.type() == Json::realValue); BEAST_EXPECT(y == Json::Value{std::numeric_limits::max()}); } { - FeeLevelDouble x{std::numeric_limits::min()}; + FeeLevelDouble const x{std::numeric_limits::min()}; auto y = x.jsonClipped(); BEAST_EXPECT(y.type() == Json::realValue); BEAST_EXPECT(y == Json::Value{std::numeric_limits::min()}); } { - XRPAmount x{std::numeric_limits::max()}; + XRPAmount const x{std::numeric_limits::max()}; auto y = x.jsonClipped(); BEAST_EXPECT(y.type() == Json::intValue); BEAST_EXPECT(y == Json::Value{std::numeric_limits::max()}); } { - XRPAmount x{std::numeric_limits::min()}; + XRPAmount const x{std::numeric_limits::min()}; auto y = x.jsonClipped(); BEAST_EXPECT(y.type() == Json::intValue); BEAST_EXPECT(y == Json::Value{std::numeric_limits::min()}); @@ -156,7 +156,7 @@ private: auto explicitmake = [&](auto x) -> FeeLevel64 { return FeeLevel64{x}; }; [[maybe_unused]] - FeeLevel64 defaulted; + FeeLevel64 const defaulted{}; FeeLevel64 test{0}; BEAST_EXPECT(test.fee() == 0); @@ -241,7 +241,7 @@ private: auto explicitmake = [&](auto x) -> FeeLevelDouble { return FeeLevelDouble{x}; }; [[maybe_unused]] - FeeLevelDouble defaulted; + FeeLevelDouble const defaulted{}; FeeLevelDouble test{0}; BEAST_EXPECT(test.fee() == 0); diff --git a/src/test/basics/XRPAmount_test.cpp b/src/test/basics/XRPAmount_test.cpp index 391917e678..ad81050558 100644 --- a/src/test/basics/XRPAmount_test.cpp +++ b/src/test/basics/XRPAmount_test.cpp @@ -16,11 +16,17 @@ public: XRPAmount const x(i); if (i < 0) + { BEAST_EXPECT(x.signum() < 0); + } else if (i > 0) + { BEAST_EXPECT(x.signum() > 0); + } else + { BEAST_EXPECT(x.signum() == 0); + } } } @@ -121,7 +127,7 @@ public: // since some of them are templated, but not used anywhere else. auto make = [&](auto x) -> XRPAmount { return XRPAmount{x}; }; - XRPAmount defaulted; + XRPAmount const defaulted{}; (void)defaulted; XRPAmount test{0}; BEAST_EXPECT(test.drops() == 0); @@ -154,7 +160,7 @@ public: BEAST_EXPECT(test.drops() == 200); auto testOther = test.dropsAs(); BEAST_EXPECT(testOther); - BEAST_EXPECT(*testOther == 200); + BEAST_EXPECT(*testOther == 200); // NOLINT(bugprone-unchecked-optional-access) test = std::numeric_limits::max(); testOther = test.dropsAs(); BEAST_EXPECT(!testOther); @@ -224,7 +230,8 @@ public: { // Similar test as above, but for negative values - XRPAmount big(minXRP); + XRPAmount big(minXRP); // NOLINT(misc-const-correctness): const breaks overflow check + // at end of this scope BEAST_EXPECT(big == mulRatio(big, maxUInt32, maxUInt32, true)); // rounding mode shouldn't matter as the result is exact BEAST_EXPECT(big == mulRatio(big, maxUInt32, maxUInt32, false)); @@ -238,7 +245,7 @@ public: { // small amounts - XRPAmount tiny(1); + XRPAmount const tiny(1); // Round up should give the smallest allowable number BEAST_EXPECT(tiny == mulRatio(tiny, 1, maxUInt32, true)); // rounding down should be zero @@ -246,7 +253,7 @@ public: BEAST_EXPECT(beast::zero == mulRatio(tiny, maxUInt32 - 1, maxUInt32, false)); // tiny negative numbers - XRPAmount tinyNeg(-1); + XRPAmount const tinyNeg(-1); // Round up should give zero BEAST_EXPECT(beast::zero == mulRatio(tinyNeg, 1, maxUInt32, true)); BEAST_EXPECT(beast::zero == mulRatio(tinyNeg, maxUInt32 - 1, maxUInt32, true)); @@ -256,21 +263,21 @@ public: { // rounding { - XRPAmount one(1); + XRPAmount const one(1); auto const rup = mulRatio(one, maxUInt32 - 1, maxUInt32, true); auto const rdown = mulRatio(one, maxUInt32 - 1, maxUInt32, false); BEAST_EXPECT(rup.drops() - rdown.drops() == 1); } { - XRPAmount big(maxXRP); + XRPAmount const big(maxXRP); auto const rup = mulRatio(big, maxUInt32 - 1, maxUInt32, true); auto const rdown = mulRatio(big, maxUInt32 - 1, maxUInt32, false); BEAST_EXPECT(rup.drops() - rdown.drops() == 1); } { - XRPAmount negOne(-1); + XRPAmount const negOne(-1); auto const rup = mulRatio(negOne, maxUInt32 - 1, maxUInt32, true); auto const rdown = mulRatio(negOne, maxUInt32 - 1, maxUInt32, false); BEAST_EXPECT(rup.drops() - rdown.drops() == 1); @@ -291,7 +298,7 @@ public: { // underflow - XRPAmount bigNegative(minXRP + 10); + XRPAmount const bigNegative(minXRP + 10); BEAST_EXPECT(mulRatio(bigNegative, 2, 1, true) == minXRP); } } // namespace xrpl diff --git a/src/test/basics/base58_test.cpp b/src/test/basics/base58_test.cpp index 46e0c7615e..52d06b324d 100644 --- a/src/test/basics/base58_test.cpp +++ b/src/test/basics/base58_test.cpp @@ -162,7 +162,7 @@ class base58_test : public beast::unit_test::suite for (int i = 0; i < iters; ++i) { std::uint64_t const d = dist(eng); - if (!d) + if (d == 0u) continue; auto bigInt = multiprecision_utils::randomBigInt(); auto const boostBigInt = multiprecision_utils::toBoostMP( @@ -270,7 +270,7 @@ class base58_test : public beast::unit_test::suite } else { - std::array tmpBuf; + std::array tmpBuf{}; std::string const s = xrpl::b58_ref::detail::encodeBase58( b256Data.data(), b256Data.size(), tmpBuf.data(), tmpBuf.size()); BEAST_EXPECT(s.size()); @@ -394,7 +394,7 @@ class base58_test : public beast::unit_test::suite // bytes range from 0-255 for (int i = 0; i < numTokenTypeIndexes; ++i) { - std::array b256DataBuf; + std::array b256DataBuf{}; auto const [tokType, tokSize] = tokenTypeAndSize(i); for (int d = 0; d <= 255; ++d) { @@ -407,7 +407,7 @@ class base58_test : public beast::unit_test::suite constexpr std::size_t iters = 100000; for (int i = 0; i < iters; ++i) { - std::array b256DataBuf; + std::array b256DataBuf{}; auto const [tokType, b256Data] = randomB256TestData(b256DataBuf); testIt(tokType, b256Data); } diff --git a/src/test/basics/base_uint_test.cpp b/src/test/basics/base_uint_test.cpp index 0aa89bcecc..139c635e5f 100644 --- a/src/test/basics/base_uint_test.cpp +++ b/src/test/basics/base_uint_test.cpp @@ -120,7 +120,7 @@ struct base_uint_test : beast::unit_test::suite // used to verify set insertion (hashing required) std::unordered_set> uset; - Blob raw{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + Blob const raw{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; BEAST_EXPECT(test96::bytes == raw.size()); test96 u{raw}; @@ -142,9 +142,9 @@ struct base_uint_test : beast::unit_test::suite // Test hash_append by "hashing" with a no-op hasher (h) // and then extracting the bytes that were written during hashing // back into another base_uint (w) for comparison with the original - nonhash<96> h; + nonhash<96> h{}; hash_append(h, u); - test96 w{std::vector(h.data_.begin(), h.data_.end())}; + test96 const w{std::vector(h.data_.begin(), h.data_.end())}; BEAST_EXPECT(w == u); test96 v{~u}; @@ -200,7 +200,7 @@ struct base_uint_test : beast::unit_test::suite zp1++; test96 zm1{z}; zm1--; - test96 x{zm1 ^ zp1}; + test96 const x{zm1 ^ zp1}; uset.insert(x); BEAST_EXPECTS(to_string(x) == "FFFFFFFFFFFFFFFFFFFFFFFE", to_string(x)); BEAST_EXPECTS(to_short_string(x) == "FFFFFFFF...", to_short_string(x)); @@ -285,8 +285,8 @@ struct base_uint_test : beast::unit_test::suite { // Try to prevent constant evaluation. std::vector str(23, '7'); - std::string_view sView(str.data(), str.size()); - [[maybe_unused]] test96 t96(sView); + std::string_view const sView(str.data(), str.size()); + [[maybe_unused]] test96 const t96(sView); } catch (std::invalid_argument const& e) { @@ -303,8 +303,8 @@ struct base_uint_test : beast::unit_test::suite // Try to prevent constant evaluation. std::vector str(23, '7'); str.push_back('G'); - std::string_view sView(str.data(), str.size()); - [[maybe_unused]] test96 t96(sView); + std::string_view const sView(str.data(), str.size()); + [[maybe_unused]] test96 const t96(sView); } catch (std::range_error const& e) { diff --git a/src/test/basics/hardened_hash_test.cpp b/src/test/basics/hardened_hash_test.cpp index 1a6609ac29..3910e5e414 100644 --- a/src/test/basics/hardened_hash_test.cpp +++ b/src/test/basics/hardened_hash_test.cpp @@ -189,13 +189,13 @@ public: check_container() { { - C> c; + C> const c; } pass(); { - C> c; + C> const c; } pass(); diff --git a/src/test/beast/IPEndpoint_test.cpp b/src/test/beast/IPEndpoint_test.cpp index e0eedd4c24..ce01743896 100644 --- a/src/test/beast/IPEndpoint_test.cpp +++ b/src/test/beast/IPEndpoint_test.cpp @@ -51,7 +51,7 @@ public: BEAST_EXPECT(AddressV4{0x01020304}.to_uint() == 0x01020304); { - AddressV4::bytes_type d = {{1, 2, 3, 4}}; + AddressV4::bytes_type const d = {{1, 2, 3, 4}}; BEAST_EXPECT(AddressV4{d}.to_uint() == 0x01020304); unexpected(is_unspecified(AddressV4{d})); @@ -110,7 +110,7 @@ public: { testcase("AddressV4::Bytes"); - AddressV4::bytes_type d1 = {{10, 0, 0, 1}}; + AddressV4::bytes_type const d1 = {{10, 0, 0, 1}}; AddressV4 v4{d1}; BEAST_EXPECT(v4.to_bytes()[0] == 10); BEAST_EXPECT(v4.to_bytes()[1] == 0); @@ -136,8 +136,8 @@ public: testcase("Address"); boost::system::error_code ec; - Address result{boost::asio::ip::make_address("1.2.3.4", ec)}; - AddressV4::bytes_type d = {{1, 2, 3, 4}}; + Address const result{boost::asio::ip::make_address("1.2.3.4", ec)}; + AddressV4::bytes_type const d = {{1, 2, 3, 4}}; BEAST_EXPECT(!ec); BEAST_EXPECT(result.is_v4() && result.to_v4() == AddressV4{d}); } @@ -152,7 +152,7 @@ public: std::string const& normal = "") { auto const result = Endpoint::from_string_checked(s); - if (!BEAST_EXPECT(result)) + if (BEAST_EXPECT(result); !result.has_value()) return; if (!BEAST_EXPECT(result->address().is_v4())) return; @@ -171,7 +171,7 @@ public: std::string const& normal = "") { auto result = Endpoint::from_string_checked(s); - if (!BEAST_EXPECT(result)) + if (BEAST_EXPECT(result); !result.has_value()) return; if (!BEAST_EXPECT(result->address().is_v6())) return; @@ -286,7 +286,7 @@ public: BEAST_EXPECTS(to_string(ep) == "::ffff:166.78.151.147", to_string(ep)); // a private IPv6 - AddressV6::bytes_type d2 = {{253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}; + AddressV6::bytes_type const d2 = {{253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}; ep = Endpoint(AddressV6{d2}); BEAST_EXPECT(!is_unspecified(ep)); BEAST_EXPECT(!is_public(ep)); diff --git a/src/test/beast/LexicalCast_test.cpp b/src/test/beast/LexicalCast_test.cpp index 4d55b95278..aa3ccfe64e 100644 --- a/src/test/beast/LexicalCast_test.cpp +++ b/src/test/beast/LexicalCast_test.cpp @@ -198,7 +198,7 @@ public: testcase("zero conversion"); { - std::int32_t out; + std::int32_t out = 0; expect(lexicalCastChecked(out, "-0"), "0"); expect(lexicalCastChecked(out, "0"), "0"); @@ -206,7 +206,7 @@ public: } { - std::uint32_t out; + std::uint32_t out = 0; expect(!lexicalCastChecked(out, "-0"), "0"); expect(lexicalCastChecked(out, "0"), "0"); @@ -220,11 +220,11 @@ public: testcase("entire range"); std::int32_t i = std::numeric_limits::min(); - std::string const empty(""); + std::string const empty; while (i <= std::numeric_limits::max()) { - std::int16_t j = static_cast(i); + std::int16_t const j = static_cast(i); auto actual = std::to_string(j); diff --git a/src/test/beast/aged_associative_container_test.cpp b/src/test/beast/aged_associative_container_test.cpp index 690f03cd49..c47b41478b 100644 --- a/src/test/beast/aged_associative_container_test.cpp +++ b/src/test/beast/aged_associative_container_test.cpp @@ -523,7 +523,7 @@ public: testArrayCreate(); template - typename std::enable_if::type + typename std::enable_if::type testArrayCreate() { } @@ -915,7 +915,7 @@ typename std::enable_if::type aged_associative_container_test_base::testConstructInitList() { using Traits = TestTraits; - typename Traits::ManualClock clock; + typename Traits::ManualClock const clock; // testcase (Traits::name() + " init-list"); testcase("init-list"); @@ -931,7 +931,7 @@ typename std::enable_if::type aged_associative_container_test_base::testConstructInitList() { using Traits = TestTraits; - typename Traits::ManualClock clock; + typename Traits::ManualClock const clock; // testcase (Traits::name() + " init-list"); testcase("init-list"); @@ -1033,8 +1033,8 @@ aged_associative_container_test_base::testIterator() using const_iterator = decltype(c.cbegin()); // Should be able to construct or assign an iterator from an iterator. - iterator nnIt_0{c.begin()}; - iterator nnIt_1{nnIt_0}; + iterator const nnIt_0{c.begin()}; + iterator const nnIt_1{nnIt_0}; BEAST_EXPECT(nnIt_0 == nnIt_1); iterator nnIt_2; nnIt_2 = nnIt_1; @@ -1042,8 +1042,8 @@ aged_associative_container_test_base::testIterator() // Should be able to construct or assign a const_iterator from a // const_iterator. - const_iterator ccIt_0{c.cbegin()}; - const_iterator ccIt_1{ccIt_0}; + const_iterator const ccIt_0{c.cbegin()}; + const_iterator const ccIt_1{ccIt_0}; BEAST_EXPECT(ccIt_0 == ccIt_1); const_iterator ccIt_2; ccIt_2 = ccIt_1; @@ -1054,8 +1054,8 @@ aged_associative_container_test_base::testIterator() BEAST_EXPECT(ccIt_1 == nnIt_1); // Should be able to construct a const_iterator from an iterator. - const_iterator ncIt_3{c.begin()}; - const_iterator ncIt_4{nnIt_0}; + const_iterator const ncIt_3{c.begin()}; + const_iterator const ncIt_4{nnIt_0}; BEAST_EXPECT(ncIt_3 == ncIt_4); const_iterator ncIt_5; ncIt_5 = nnIt_2; @@ -1098,8 +1098,8 @@ aged_associative_container_test_base::testReverseIterator() // Should be able to construct or assign a reverse_iterator from a // reverse_iterator. - reverse_iterator rNrNit_0{c.rbegin()}; - reverse_iterator rNrNit_1{rNrNit_0}; + reverse_iterator const rNrNit_0{c.rbegin()}; + reverse_iterator const rNrNit_1{rNrNit_0}; BEAST_EXPECT(rNrNit_0 == rNrNit_1); reverse_iterator xXrNit_2; xXrNit_2 = rNrNit_1; @@ -1107,8 +1107,8 @@ aged_associative_container_test_base::testReverseIterator() // Should be able to construct or assign a const_reverse_iterator from a // const_reverse_iterator - const_reverse_iterator rCrCit_0{c.crbegin()}; - const_reverse_iterator rCrCit_1{rCrCit_0}; + const_reverse_iterator const rCrCit_0{c.crbegin()}; + const_reverse_iterator const rCrCit_1{rCrCit_0}; BEAST_EXPECT(rCrCit_0 == rCrCit_1); const_reverse_iterator xXrCit_2; xXrCit_2 = rCrCit_1; @@ -1120,8 +1120,8 @@ aged_associative_container_test_base::testReverseIterator() // Should be able to construct or assign a const_reverse_iterator from a // reverse_iterator - const_reverse_iterator rNrCit_0{c.rbegin()}; - const_reverse_iterator rNrCit_1{rNrNit_0}; + const_reverse_iterator const rNrCit_0{c.rbegin()}; + const_reverse_iterator const rNrCit_1{rNrNit_0}; BEAST_EXPECT(rNrCit_0 == rNrCit_1); xXrCit_2 = rNrNit_1; BEAST_EXPECT(rNrCit_1 == xXrCit_2); @@ -1132,10 +1132,10 @@ aged_associative_container_test_base::testReverseIterator() // const_iterator. // Should be able to construct or assign reverse_iterators from // non-reverse iterators. - reverse_iterator fNrNit_0{c.begin()}; - const_reverse_iterator fNrCit_0{c.begin()}; + reverse_iterator const fNrNit_0{c.begin()}; + const_reverse_iterator const fNrCit_0{c.begin()}; BEAST_EXPECT(fNrNit_0 == fNrCit_0); - const_reverse_iterator fCrCit_0{c.cbegin()}; + const_reverse_iterator const fCrCit_0{c.cbegin()}; BEAST_EXPECT(fNrCit_0 == fCrCit_0); // None of these should compile because they construct a non-reverse @@ -1146,7 +1146,7 @@ aged_associative_container_test_base::testReverseIterator() // You should not be able to assign an iterator to a reverse_iterator or // vise-versa. So the following lines should not compile. - iterator xXfNit_0; + iterator const xXfNit_0; // xXfNit_0 = xXrNit_2; // xXrNit_2 = xXfNit_0; } @@ -1297,7 +1297,7 @@ aged_associative_container_test_base::testChronological() for (auto iter(v.crbegin()); iter != v.crend(); ++iter) { using iterator = typename decltype(c)::iterator; - iterator found(c.find(Traits::extract(*iter))); + iterator const found(c.find(Traits::extract(*iter))); BEAST_EXPECT(found != c.cend()); if (found == c.cend()) @@ -1317,7 +1317,7 @@ aged_associative_container_test_base::testChronological() for (auto iter(v.cbegin()); iter != v.cend(); ++iter) { using const_iterator = typename decltype(c)::const_iterator; - const_iterator found(c.find(Traits::extract(*iter))); + const_iterator const found(c.find(Traits::extract(*iter))); BEAST_EXPECT(found != c.cend()); if (found == c.cend()) @@ -1362,7 +1362,7 @@ aged_associative_container_test_base::testArrayCreate() { // Copy construct key typename Traits::template Cont<> c(clock); - for (auto e : v) + for (auto const& e : v) c[e.first] = e.second; checkContents(c, v); } @@ -1642,7 +1642,7 @@ aged_associative_container_test_base::testCompare() // testcase (Traits::name() + " array create"); testcase("array create"); - typename Traits::template Cont<> c1(v.begin(), v.end(), clock); + typename Traits::template Cont<> const c1(v.begin(), v.end(), clock); typename Traits::template Cont<> c2(v.begin(), v.end(), clock); c2.erase(c2.cbegin()); @@ -1672,7 +1672,7 @@ aged_associative_container_test_base::testObservers() // testcase (Traits::name() + " observers"); testcase("observers"); - typename Traits::template Cont<> c(clock); + typename Traits::template Cont<> const c(clock); c.key_comp(); c.value_comp(); @@ -1690,7 +1690,7 @@ aged_associative_container_test_base::testObservers() // testcase (Traits::name() + " observers"); testcase("observers"); - typename Traits::template Cont<> c(clock); + typename Traits::template Cont<> const c(clock); c.hash_function(); c.key_eq(); diff --git a/src/test/beast/beast_Journal_test.cpp b/src/test/beast/beast_Journal_test.cpp index 407e9ea7aa..35ab3640bd 100644 --- a/src/test/beast/beast_Journal_test.cpp +++ b/src/test/beast/beast_Journal_test.cpp @@ -9,10 +9,10 @@ public: class TestSink : public Journal::Sink { private: - int m_count; + int m_count{0}; public: - TestSink() : Sink(severities::kWarning, false), m_count(0) + TestSink() : Sink(severities::kWarning, false) { } @@ -50,7 +50,7 @@ public: using namespace beast::severities; sink.threshold(kInfo); - Journal j(sink); + Journal const j(sink); j.trace() << " "; BEAST_EXPECT(sink.count() == 0); diff --git a/src/test/beast/beast_PropertyStream_test.cpp b/src/test/beast/beast_PropertyStream_test.cpp index 35aa91d18e..9e76749218 100644 --- a/src/test/beast/beast_PropertyStream_test.cpp +++ b/src/test/beast/beast_PropertyStream_test.cpp @@ -67,7 +67,7 @@ public: { try { - Source* source(root.find_one(name)); + Source const* source(root.find_one(name)); BEAST_EXPECT(source == expected); } catch (...) @@ -82,7 +82,7 @@ public: { try { - Source* source(root.find_path(path)); + Source const* source(root.find_path(path)); BEAST_EXPECT(source == expected); } catch (...) @@ -97,7 +97,7 @@ public: { try { - Source* source(root.find_one_deep(name)); + Source const* source(root.find_one_deep(name)); BEAST_EXPECT(source == expected); } catch (...) diff --git a/src/test/beast/beast_io_latency_probe_test.cpp b/src/test/beast/beast_io_latency_probe_test.cpp index f2423550d3..dfa18e6770 100644 --- a/src/test/beast/beast_io_latency_probe_test.cpp +++ b/src/test/beast/beast_io_latency_probe_test.cpp @@ -50,7 +50,7 @@ class io_latency_probe_test : public beast::unit_test::suite, public beast::test bool done = false; boost::system::error_code wait_err; - while (--num_samples) + while (--num_samples > 0u) { auto const start{MeasureClock::now()}; done = false; @@ -60,7 +60,7 @@ class io_latency_probe_test : public beast::unit_test::suite, public beast::test wait_err = ec; auto const end{MeasureClock::now()}; elapsed_times_.emplace_back(end - start); - std::lock_guard lk{mtx}; + std::lock_guard const lk{mtx}; done = true; cv.notify_one(); }); @@ -157,7 +157,8 @@ class io_latency_probe_test : public beast::unit_test::suite, public beast::test auto interval = 99ms; auto probe_duration = 1s; - size_t expected_probe_count_max = (probe_duration / interval); + size_t const expected_probe_count_max = (probe_duration / interval); + // NOLINTNEXTLINE(misc-const-correctness) size_t expected_probe_count_min = expected_probe_count_max; #ifdef XRPL_RUNNING_IN_CI // adjust min expected based on measurements diff --git a/src/test/conditions/PreimageSha256_test.cpp b/src/test/conditions/PreimageSha256_test.cpp index c3508758a7..374b6eb925 100644 --- a/src/test/conditions/PreimageSha256_test.cpp +++ b/src/test/conditions/PreimageSha256_test.cpp @@ -17,12 +17,12 @@ namespace cryptoconditions { class PreimageSha256_test : public beast::unit_test::suite { - inline Buffer + Buffer hexblob(std::string const& s) { auto blob = strUnHex(s); BEAST_EXPECT(blob); - return {blob->data(), blob->size()}; + return {blob->data(), blob->size()}; // NOLINT(bugprone-unchecked-optional-access) } void @@ -149,7 +149,7 @@ class PreimageSha256_test : public beast::unit_test::suite "9CF1D5F810302" "0000"}}; - for (auto x : others) + for (auto const& x : others) { std::error_code ec; diff --git a/src/test/consensus/ByzantineFailureSim_test.cpp b/src/test/consensus/ByzantineFailureSim_test.cpp index 245c52d9e3..f86ae556bf 100644 --- a/src/test/consensus/ByzantineFailureSim_test.cpp +++ b/src/test/consensus/ByzantineFailureSim_test.cpp @@ -39,7 +39,7 @@ class ByzantineFailureSim_test : public beast::unit_test::suite f.trustAndConnect(f + d + e + g, delay); g.trustAndConnect(g + a + f, delay); - PeerGroup network = a + b + c + d + e + f + g; + PeerGroup const network = a + b + c + d + e + f + g; StreamCollector sc{std::cout}; diff --git a/src/test/consensus/Consensus_test.cpp b/src/test/consensus/Consensus_test.cpp index 0d023f270d..8b562454e3 100644 --- a/src/test/consensus/Consensus_test.cpp +++ b/src/test/consensus/Consensus_test.cpp @@ -144,7 +144,7 @@ public: testcase("standalone"); Sim s; - PeerGroup peers = s.createGroup(1); + PeerGroup const peers = s.createGroup(1); Peer* peer = peers[0]; peer->targetLedgers = 1; peer->start(); @@ -157,7 +157,7 @@ public: BEAST_EXPECT(peer->prevLedgerID() == lcl.id()); BEAST_EXPECT(lcl.seq() == Ledger::Seq{1}); BEAST_EXPECT(lcl.txs().size() == 1); - BEAST_EXPECT(lcl.txs().find(Tx{1}) != lcl.txs().end()); + BEAST_EXPECT(lcl.txs().contains(Tx{1})); BEAST_EXPECT(peer->prevProposers == 0); } @@ -193,7 +193,7 @@ public: BEAST_EXPECT(peer->prevProposers == peers.size() - 1); // All transactions were accepted for (std::uint32_t i = 0; i < peers.size(); ++i) - BEAST_EXPECT(lcl.txs().find(Tx{i}) != lcl.txs().end()); + BEAST_EXPECT(lcl.txs().contains(Tx{i})); } } } @@ -235,7 +235,7 @@ public: // All peers are in sync even with a slower peer 0 if (BEAST_EXPECT(sim.synchronized())) { - for (Peer* peer : network) + for (Peer const* peer : network) { auto const& lcl = peer->lastClosedLedger; BEAST_EXPECT(lcl.id() == peer->prevLedgerID()); @@ -244,12 +244,12 @@ public: BEAST_EXPECT(peer->prevProposers == network.size() - 1); BEAST_EXPECT(peer->prevRoundTime == network[0]->prevRoundTime); - BEAST_EXPECT(lcl.txs().find(Tx{0}) == lcl.txs().end()); + BEAST_EXPECT(not lcl.txs().contains(Tx{0})); for (std::uint32_t i = 2; i < network.size(); ++i) - BEAST_EXPECT(lcl.txs().find(Tx{i}) != lcl.txs().end()); + BEAST_EXPECT(lcl.txs().contains(Tx{i})); // Tx 0 didn't make it - BEAST_EXPECT(peer->openTxs.find(Tx{0}) != peer->openTxs.end()); + BEAST_EXPECT(peer->openTxs.contains(Tx{0})); } } } @@ -292,28 +292,32 @@ public: // Verify all peers have same LCL but are missing // transaction 0,1 which was not received by all peers // before the ledger closed - for (Peer* peer : network) + for (Peer const* peer : network) { // Closed ledger has all but transaction 0,1 auto const& lcl = peer->lastClosedLedger; BEAST_EXPECT(lcl.seq() == Ledger::Seq{1}); - BEAST_EXPECT(lcl.txs().find(Tx{0}) == lcl.txs().end()); - BEAST_EXPECT(lcl.txs().find(Tx{1}) == lcl.txs().end()); + BEAST_EXPECT(not lcl.txs().contains(Tx{0})); + BEAST_EXPECT(not lcl.txs().contains(Tx{1})); for (std::uint32_t i = slow.size(); i < network.size(); ++i) - BEAST_EXPECT(lcl.txs().find(Tx{i}) != lcl.txs().end()); + BEAST_EXPECT(lcl.txs().contains(Tx{i})); // Tx 0-1 didn't make it - BEAST_EXPECT(peer->openTxs.find(Tx{0}) != peer->openTxs.end()); - BEAST_EXPECT(peer->openTxs.find(Tx{1}) != peer->openTxs.end()); + BEAST_EXPECT(peer->openTxs.contains(Tx{0})); + BEAST_EXPECT(peer->openTxs.contains(Tx{1})); } Peer const* slowPeer = slow[0]; if (isParticipant) + { BEAST_EXPECT(slowPeer->prevProposers == network.size() - 1); + } else + { BEAST_EXPECT(slowPeer->prevProposers == fast.size()); + } - for (Peer* peer : fast) + for (Peer const* peer : fast) { // Due to the network link delay settings // Peer 0 initially proposes {0} @@ -384,8 +388,8 @@ public: Sim sim; PeerGroup groupA = sim.createGroup(2); - PeerGroup groupB = sim.createGroup(2); - PeerGroup groupC = sim.createGroup(2); + PeerGroup const groupB = sim.createGroup(2); + PeerGroup const groupC = sim.createGroup(2); PeerGroup network = groupA + groupB + groupC; network.trust(network); @@ -393,7 +397,7 @@ public: // Run consensus without skew until we have a short close time // resolution - Peer* firstPeer = *groupA.begin(); + Peer const* firstPeer = *groupA.begin(); while (firstPeer->lastClosedLedger.closeTimeResolution() >= parms.proposeFRESHNESS) sim.run(1); @@ -408,7 +412,7 @@ public: // All nodes agreed to disagree on the close time if (BEAST_EXPECT(sim.synchronized())) { - for (Peer* peer : network) + for (Peer const* peer : network) BEAST_EXPECT(!peer->lastClosedLedger.closeAgree()); } } @@ -453,13 +457,13 @@ public: Sim sim; PeerGroup minority = sim.createGroup(2); - PeerGroup majorityA = sim.createGroup(3); - PeerGroup majorityB = sim.createGroup(5); + PeerGroup const majorityA = sim.createGroup(3); + PeerGroup const majorityB = sim.createGroup(5); PeerGroup majority = majorityA + majorityB; - PeerGroup network = minority + majority; + PeerGroup const network = minority + majority; - SimDuration delay = round(0.2 * parms.ledgerGRANULARITY); + SimDuration const delay = round(0.2 * parms.ledgerGRANULARITY); minority.trustAndConnect(minority + majorityA, delay); majority.trustAndConnect(majority, delay); @@ -552,10 +556,10 @@ public: Sim sim; PeerGroup loner = sim.createGroup(1); - PeerGroup friends = sim.createGroup(3); + PeerGroup const friends = sim.createGroup(3); loner.trust(loner + friends); - PeerGroup others = sim.createGroup(6); + PeerGroup const others = sim.createGroup(6); PeerGroup clique = friends + others; clique.trust(clique); @@ -577,7 +581,7 @@ public: sim.run(2); // Check all peers recovered - for (Peer* p : network) + for (Peer const* p : network) BEAST_EXPECT(p->prevLedgerID() == network[0]->prevLedgerID()); } } @@ -592,7 +596,7 @@ public: // This is a specialized test engineered to yield ledgers with different // close times even though the peers believe they had close time // consensus on the ledger. - ConsensusParms parms; + ConsensusParms const parms; Sim sim; @@ -630,7 +634,7 @@ public: NetClock::duration when = network[0]->now().time_since_epoch(); // Check we are before the 30s to 20s transition - NetClock::duration resolution = network[0]->lastClosedLedger.closeTimeResolution(); + NetClock::duration const resolution = network[0]->lastClosedLedger.closeTimeResolution(); BEAST_EXPECT(resolution == NetClock::duration{30s}); while (((when % NetClock::duration{30s}) != NetClock::duration{15s}) || @@ -646,7 +650,7 @@ public: { // close time should be ahead of clock time since we engineered // the close time to round up - for (Peer* peer : network) + for (Peer const* peer : network) { BEAST_EXPECT(peer->lastClosedLedger.closeTime() > peer->now()); BEAST_EXPECT(peer->lastClosedLedger.closeAgree()); @@ -688,26 +692,26 @@ public: using namespace std::chrono; testcase("fork"); - std::uint32_t numPeers = 10; + std::uint32_t const numPeers = 10; // Vary overlap between two UNLs for (std::uint32_t overlap = 0; overlap <= numPeers; ++overlap) { ConsensusParms const parms{}; Sim sim; - std::uint32_t numA = (numPeers - overlap) / 2; - std::uint32_t numB = numPeers - numA - overlap; + std::uint32_t const numA = (numPeers - overlap) / 2; + std::uint32_t const numB = numPeers - numA - overlap; - PeerGroup aOnly = sim.createGroup(numA); - PeerGroup bOnly = sim.createGroup(numB); - PeerGroup commonOnly = sim.createGroup(overlap); + PeerGroup const aOnly = sim.createGroup(numA); + PeerGroup const bOnly = sim.createGroup(numB); + PeerGroup const commonOnly = sim.createGroup(overlap); PeerGroup a = aOnly + commonOnly; PeerGroup b = bOnly + commonOnly; - PeerGroup network = a + b; + PeerGroup const network = a + b; - SimDuration delay = round(0.2 * parms.ledgerGRANULARITY); + SimDuration const delay = round(0.2 * parms.ledgerGRANULARITY); a.trustAndConnect(a, delay); b.trustAndConnect(b, delay); @@ -717,7 +721,7 @@ public: { // Nodes have only seen transactions from their neighbors peer->openTxs.insert(Tx{static_cast(peer->id)}); - for (Peer* to : sim.trustGraph.trustedPeers(peer)) + for (Peer const* to : sim.trustGraph.trustedPeers(peer)) peer->openTxs.insert(Tx{static_cast(to->id)}); } sim.run(1); @@ -726,7 +730,9 @@ public: // Since the overlapped nodes have a UNL that is the union of the // two cliques, the maximum sized UNL list is the number of peers if (overlap > 0.4 * numPeers) + { BEAST_EXPECT(sim.synchronized()); + } else { // Even if we do fork, there shouldn't be more than 3 ledgers @@ -753,7 +759,7 @@ public: validators.trust(validators); center.trust(validators); - SimDuration delay = round(0.2 * parms.ledgerGRANULARITY); + SimDuration const delay = round(0.2 * parms.ledgerGRANULARITY); validators.connect(center, delay); center[0]->runAsValidator = false; @@ -860,7 +866,7 @@ public: Sim sim; // Goes A->B->D - PeerGroup groupABD = sim.createGroup(2); + PeerGroup const groupABD = sim.createGroup(2); // Single node that initially fully validates C before the split PeerGroup groupCfast = sim.createGroup(1); // Generates C, but fails to fully validate before the split @@ -869,8 +875,8 @@ public: PeerGroup groupNotFastC = groupABD + groupCsplit; PeerGroup network = groupABD + groupCsplit + groupCfast; - SimDuration delay = round(0.2 * parms.ledgerGRANULARITY); - SimDuration fDelay = round(0.1 * parms.ledgerGRANULARITY); + SimDuration const delay = round(0.2 * parms.ledgerGRANULARITY); + SimDuration const fDelay = round(0.1 * parms.ledgerGRANULARITY); network.trust(network); // C must have a shorter delay to see all the validations before the @@ -981,14 +987,14 @@ public: ConsensusParms const parms{}; Sim sim; - SimDuration delay = round(0.2 * parms.ledgerGRANULARITY); + SimDuration const delay = round(0.2 * parms.ledgerGRANULARITY); PeerGroup behind = sim.createGroup(3); - PeerGroup ahead = sim.createGroup(2); + PeerGroup const ahead = sim.createGroup(2); PeerGroup network = ahead + behind; hash_set trustedKeys; - for (Peer* p : network) + for (Peer const* p : network) trustedKeys.insert(p->key); for (Peer* p : network) p->trustedKeys = trustedKeys; @@ -1055,7 +1061,7 @@ public: Tx const txFollowingTrue{97}; Tx const txFollowingFalse{96}; int const numPeers = 100; - ConsensusParms p; + ConsensusParms const p; std::size_t peersUnchanged = 0; auto logs = std::make_unique(beast::severities::kError); @@ -1100,7 +1106,7 @@ public: BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged, j, clog)); BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged, j, clog)); BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged, j, clog)); - BEAST_EXPECT(clog->str() == ""); + BEAST_EXPECT(clog->str().empty()); // I'm in the majority, my vote should not change BEAST_EXPECT(!proposingTrue.updateVote(5, true, p)); @@ -1118,7 +1124,7 @@ public: BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged, j, clog)); BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged, j, clog)); BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged, j, clog)); - BEAST_EXPECT(clog->str() == ""); + BEAST_EXPECT(clog->str().empty()); // Right now, the vote is 51%. The requirement is about to jump to // 65% @@ -1212,7 +1218,7 @@ public: BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged, j, clog)); BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged, j, clog)); BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged, j, clog)); - BEAST_EXPECT(clog->str() == ""); + BEAST_EXPECT(clog->str().empty()); // Threshold jumps to 95% BEAST_EXPECT(proposingTrue.updateVote(220, true, p)); @@ -1253,7 +1259,7 @@ public: BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged, j, clog)); BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged, j, clog)); BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged, j, clog)); - BEAST_EXPECT(clog->str() == ""); + BEAST_EXPECT(clog->str().empty()); } auto expectStalled = [this, &clog]( @@ -1289,10 +1295,10 @@ public: for (int i = 0; i < 1; ++i) { - BEAST_EXPECT(!proposingTrue.updateVote(250 + 10 * i, true, p)); - BEAST_EXPECT(!proposingFalse.updateVote(250 + 10 * i, true, p)); - BEAST_EXPECT(!followingTrue.updateVote(250 + 10 * i, false, p)); - BEAST_EXPECT(!followingFalse.updateVote(250 + 10 * i, false, p)); + BEAST_EXPECT(!proposingTrue.updateVote(250 + (10 * i), true, p)); + BEAST_EXPECT(!proposingFalse.updateVote(250 + (10 * i), true, p)); + BEAST_EXPECT(!followingTrue.updateVote(250 + (10 * i), false, p)); + BEAST_EXPECT(!followingFalse.updateVote(250 + (10 * i), false, p)); BEAST_EXPECT(proposingTrue.getOurVote() == true); BEAST_EXPECT(proposingFalse.getOurVote() == false); @@ -1301,7 +1307,7 @@ public: // true vote has changed recently, so not stalled BEAST_EXPECT(!proposingTrue.stalled(p, true, 0, j, clog)); - BEAST_EXPECT(clog->str() == ""); + BEAST_EXPECT(clog->str().empty()); // remaining votes have been unchanged in so long that we only // need to hit the second round at 95% to be stalled, regardless // of peers @@ -1314,7 +1320,7 @@ public: // true vote has changed recently, so not stalled BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged, j, clog)); - BEAST_EXPECTS(clog->str() == "", clog->str()); + BEAST_EXPECTS(clog->str().empty(), clog->str()); // remaining votes have been unchanged in so long that we only // need to hit the second round at 95% to be stalled, regardless // of peers @@ -1327,10 +1333,10 @@ public: } for (int i = 1; i < 3; ++i) { - BEAST_EXPECT(!proposingTrue.updateVote(250 + 10 * i, true, p)); - BEAST_EXPECT(!proposingFalse.updateVote(250 + 10 * i, true, p)); - BEAST_EXPECT(!followingTrue.updateVote(250 + 10 * i, false, p)); - BEAST_EXPECT(!followingFalse.updateVote(250 + 10 * i, false, p)); + BEAST_EXPECT(!proposingTrue.updateVote(250 + (10 * i), true, p)); + BEAST_EXPECT(!proposingFalse.updateVote(250 + (10 * i), true, p)); + BEAST_EXPECT(!followingTrue.updateVote(250 + (10 * i), false, p)); + BEAST_EXPECT(!followingFalse.updateVote(250 + (10 * i), false, p)); BEAST_EXPECT(proposingTrue.getOurVote() == true); BEAST_EXPECT(proposingFalse.getOurVote() == false); @@ -1340,7 +1346,7 @@ public: // true vote changed 2 rounds ago, and peers are changing, so // not stalled BEAST_EXPECT(!proposingTrue.stalled(p, true, 0, j, clog)); - BEAST_EXPECTS(clog->str() == "", clog->str()); + BEAST_EXPECTS(clog->str().empty(), clog->str()); // still stalled BEAST_EXPECT(proposingFalse.stalled(p, true, 0, j, clog)); expectStalled(98, false, 11 + i, 0, 2, __LINE__); @@ -1363,10 +1369,10 @@ public: } for (int i = 3; i < 5; ++i) { - BEAST_EXPECT(!proposingTrue.updateVote(250 + 10 * i, true, p)); - BEAST_EXPECT(!proposingFalse.updateVote(250 + 10 * i, true, p)); - BEAST_EXPECT(!followingTrue.updateVote(250 + 10 * i, false, p)); - BEAST_EXPECT(!followingFalse.updateVote(250 + 10 * i, false, p)); + BEAST_EXPECT(!proposingTrue.updateVote(250 + (10 * i), true, p)); + BEAST_EXPECT(!proposingFalse.updateVote(250 + (10 * i), true, p)); + BEAST_EXPECT(!followingTrue.updateVote(250 + (10 * i), false, p)); + BEAST_EXPECT(!followingFalse.updateVote(250 + (10 * i), false, p)); BEAST_EXPECT(proposingTrue.getOurVote() == true); BEAST_EXPECT(proposingFalse.getOurVote() == false); diff --git a/src/test/consensus/DistributedValidatorsSim_test.cpp b/src/test/consensus/DistributedValidatorsSim_test.cpp index b430a63880..f510e00628 100644 --- a/src/test/consensus/DistributedValidatorsSim_test.cpp +++ b/src/test/consensus/DistributedValidatorsSim_test.cpp @@ -155,9 +155,9 @@ class DistributedValidators_test : public beast::unit_test::suite sim.run(1); // Run for 10 minutes, submitting 100 tx/second - std::chrono::nanoseconds simDuration = 10min; - std::chrono::nanoseconds quiet = 10s; - Rate rate{100, 1000ms}; + std::chrono::nanoseconds const simDuration = 10min; + std::chrono::nanoseconds const quiet = 10s; + Rate const rate{100, 1000ms}; // Initialize timers HeartbeatTimer heart(sim.scheduler); diff --git a/src/test/consensus/LedgerTiming_test.cpp b/src/test/consensus/LedgerTiming_test.cpp index 62902d6a28..8313ffd0d4 100644 --- a/src/test/consensus/LedgerTiming_test.cpp +++ b/src/test/consensus/LedgerTiming_test.cpp @@ -1,6 +1,5 @@ -#include - #include +#include namespace xrpl { namespace test { @@ -29,11 +28,17 @@ class LedgerTiming_test : public beast::unit_test::suite nextCloseResolution = getNextLedgerTimeResolution(closeResolution, previousAgree, ++round); if (nextCloseResolution < closeResolution) + { ++res.decrease; + } else if (nextCloseResolution > closeResolution) + { ++res.increase; + } else + { ++res.equal; + } std::swap(nextCloseResolution, closeResolution); } while (round < rounds); return res; @@ -61,7 +66,7 @@ class LedgerTiming_test : public beast::unit_test::suite using namespace std::chrono_literals; // A closeTime equal to the epoch is not modified using tp = NetClock::time_point; - tp def; + tp const def; BEAST_EXPECT(def == roundCloseTime(def, 30s)); // Otherwise, the closeTime is rounded to the nearest diff --git a/src/test/consensus/LedgerTrie_test.cpp b/src/test/consensus/LedgerTrie_test.cpp index 586b9231f6..0836b9c342 100644 --- a/src/test/consensus/LedgerTrie_test.cpp +++ b/src/test/consensus/LedgerTrie_test.cpp @@ -278,7 +278,7 @@ class LedgerTrie_test : public beast::unit_test::suite LedgerHistoryHelper h; BEAST_EXPECT(t.empty()); - Ledger genesis = h[""]; + Ledger const genesis = h[""]; t.insert(genesis); BEAST_EXPECT(!t.empty()); t.remove(genesis); @@ -344,7 +344,7 @@ class LedgerTrie_test : public beast::unit_test::suite using Seq = Ledger::Seq; // Empty { - LedgerTrie t; + LedgerTrie const t; BEAST_EXPECT(t.getPreferred(Seq{0}) == std::nullopt); BEAST_EXPECT(t.getPreferred(Seq{2}) == std::nullopt); } @@ -352,8 +352,10 @@ class LedgerTrie_test : public beast::unit_test::suite { LedgerTrie t; LedgerHistoryHelper h; - Ledger genesis = h[""]; + Ledger const genesis = h[""]; t.insert(genesis); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{0})->id == genesis.id()); BEAST_EXPECT(t.remove(genesis)); BEAST_EXPECT(t.getPreferred(Seq{0}) == std::nullopt); @@ -364,6 +366,8 @@ class LedgerTrie_test : public beast::unit_test::suite LedgerTrie t; LedgerHistoryHelper h; t.insert(h["abc"]); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id()); } // Single node smaller child support @@ -372,7 +376,11 @@ class LedgerTrie_test : public beast::unit_test::suite LedgerHistoryHelper h; t.insert(h["abc"]); t.insert(h["abcd"]); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id()); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id()); } // Single node larger child @@ -381,7 +389,11 @@ class LedgerTrie_test : public beast::unit_test::suite LedgerHistoryHelper h; t.insert(h["abc"]); t.insert(h["abcd"], 2); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abcd"].id()); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcd"].id()); } // Single node smaller children support @@ -391,12 +403,16 @@ class LedgerTrie_test : public beast::unit_test::suite t.insert(h["abc"]); t.insert(h["abcd"]); t.insert(h["abce"]); + + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id()); t.insert(h["abc"]); + BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) } // Single node larger children { @@ -405,12 +421,16 @@ class LedgerTrie_test : public beast::unit_test::suite t.insert(h["abc"]); t.insert(h["abcd"], 2); t.insert(h["abce"]); + + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id()); t.insert(h["abcd"]); + BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abcd"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcd"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) } // Tie-breaker by id { @@ -420,10 +440,14 @@ class LedgerTrie_test : public beast::unit_test::suite t.insert(h["abce"], 2); BEAST_EXPECT(h["abce"].id() > h["abcd"].id()); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abce"].id()); t.insert(h["abcd"]); BEAST_EXPECT(h["abce"].id() > h["abcd"].id()); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcd"].id()); } @@ -436,14 +460,18 @@ class LedgerTrie_test : public beast::unit_test::suite t.insert(h["abce"], 2); // abce only has a margin of 1, but it owns the tie-breaker BEAST_EXPECT(h["abce"].id() > h["abcd"].id()); + + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abce"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abce"].id()); // Switch support from abce to abcd, tie-breaker now needed t.remove(h["abce"]); t.insert(h["abcd"]); + BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) } // Single node larger grand child @@ -453,9 +481,12 @@ class LedgerTrie_test : public beast::unit_test::suite t.insert(h["abc"]); t.insert(h["abcd"], 2); t.insert(h["abcde"], 4); + + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abcde"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcde"].id()); BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["abcde"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) } // Too much uncommitted support from competing branches @@ -466,6 +497,7 @@ class LedgerTrie_test : public beast::unit_test::suite t.insert(h["abcde"], 2); t.insert(h["abcfg"], 2); // 'de' and 'fg' are tied without 'abc' vote + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id()); BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["abc"].id()); @@ -473,8 +505,7 @@ class LedgerTrie_test : public beast::unit_test::suite t.remove(h["abc"]); t.insert(h["abcd"]); - // 'de' branch has 3 votes to 2, so earlier sequences see it as - // preferred + // 'de' branch has 3 votes to 2, so earlier sequences see it as preferred BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abcde"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcde"].id()); @@ -482,6 +513,7 @@ class LedgerTrie_test : public beast::unit_test::suite // a different branch, you do not yet know if they chose abcd // or abcf because of you, so abc remains preferred BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["abc"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) } // Changing largestSeq perspective changes preferred branch @@ -505,12 +537,15 @@ class LedgerTrie_test : public beast::unit_test::suite t.insert(h["abde"], 2); // B has more branch support + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{1})->id == h["ab"].id()); BEAST_EXPECT(t.getPreferred(Seq{2})->id == h["ab"].id()); + // But if you last validated D,F or E, you do not yet know // if someone used that validation to commit to B or C BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["a"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["a"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) /** One of E advancing to G doesn't change anything A @@ -526,11 +561,13 @@ class LedgerTrie_test : public beast::unit_test::suite t.remove(h["abde"]); t.insert(h["abdeg"]); + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{1})->id == h["ab"].id()); BEAST_EXPECT(t.getPreferred(Seq{2})->id == h["ab"].id()); BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["a"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["a"].id()); BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["a"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) /** C advancing to H does advance the seq 3 preferred ledger A @@ -545,11 +582,14 @@ class LedgerTrie_test : public beast::unit_test::suite */ t.remove(h["ac"]); t.insert(h["abh"]); + + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{1})->id == h["ab"].id()); BEAST_EXPECT(t.getPreferred(Seq{2})->id == h["ab"].id()); BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["ab"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["a"].id()); BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["a"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) /** F advancing to E also moves the preferred ledger forward A @@ -564,11 +604,14 @@ class LedgerTrie_test : public beast::unit_test::suite */ t.remove(h["acf"]); t.insert(h["abde"]); + + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{1})->id == h["abde"].id()); BEAST_EXPECT(t.getPreferred(Seq{2})->id == h["abde"].id()); BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abde"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["ab"].id()); BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["ab"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) } } @@ -626,21 +669,25 @@ class LedgerTrie_test : public beast::unit_test::suite for (std::uint32_t i = 0; i < iterations; ++i) { // pick a random ledger history - std::string curr = ""; - char depth = depthDist(gen); + std::string curr; + char const depth = depthDist(gen); char offset = 0; for (char d = 0; d < depth; ++d) { - char a = offset + widthDist(gen); + char const a = offset + widthDist(gen); curr += a; offset = (a + 1) * width; } // 50-50 to add remove if (flip(gen) == 0) + { t.insert(h[curr]); + } else + { t.remove(h[curr]); + } if (!BEAST_EXPECT(t.checkInvariants())) return; } diff --git a/src/test/consensus/NegativeUNL_test.cpp b/src/test/consensus/NegativeUNL_test.cpp index d03ee3950d..0f97704755 100644 --- a/src/test/consensus/NegativeUNL_test.cpp +++ b/src/test/consensus/NegativeUNL_test.cpp @@ -1,12 +1,11 @@ #include #include -#include #include #include #include -#include +#include #include namespace xrpl { @@ -211,7 +210,11 @@ class NegativeUNL_test : public beast::unit_test::suite std::vector publicKeys = createPublicKeys(3); // genesis ledger auto l = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); // Record the public keys and ledger sequences of expected negative UNL // validators when we build the ledger history @@ -219,7 +222,7 @@ class NegativeUNL_test : public beast::unit_test::suite { //(1) the ledger after genesis, not a flag ledger - l = std::make_shared(*l, env.app().timeKeeper().closeTime()); + l = std::make_shared(*l, env.app().getTimeKeeper().closeTime()); auto txDisable_0 = createTx(true, l->seq(), publicKeys[0]); auto txReEnable_1 = createTx(false, l->seq(), publicKeys[1]); @@ -236,7 +239,7 @@ class NegativeUNL_test : public beast::unit_test::suite // generate more ledgers for (auto i = 0; i < 256 - 2; ++i) { - l = std::make_shared(*l, env.app().timeKeeper().closeTime()); + l = std::make_shared(*l, env.app().getTimeKeeper().closeTime()); } BEAST_EXPECT(l->isFlagLedger()); l->updateNegativeUNL(); @@ -258,7 +261,7 @@ class NegativeUNL_test : public beast::unit_test::suite { BEAST_EXPECT(l->validatorToDisable() == publicKeys[0]); //++ first ToDisable Tx in ledger's TxSet - uint256 txID = txDisable_0.getTransactionID(); + uint256 const txID = txDisable_0.getTransactionID(); BEAST_EXPECT(l->txExists(txID)); } } @@ -271,7 +274,7 @@ class NegativeUNL_test : public beast::unit_test::suite BEAST_EXPECT(good_size); if (good_size) BEAST_EXPECT(l->validatorToDisable() == publicKeys[0]); - l = std::make_shared(*l, env.app().timeKeeper().closeTime()); + l = std::make_shared(*l, env.app().getTimeKeeper().closeTime()); } BEAST_EXPECT(l->isFlagLedger()); l->updateNegativeUNL(); @@ -323,7 +326,7 @@ class NegativeUNL_test : public beast::unit_test::suite BEAST_EXPECT(l->validatorToDisable() == publicKeys[1]); BEAST_EXPECT(l->validatorToReEnable() == publicKeys[0]); } - l = std::make_shared(*l, env.app().timeKeeper().closeTime()); + l = std::make_shared(*l, env.app().getTimeKeeper().closeTime()); } BEAST_EXPECT(l->isFlagLedger()); l->updateNegativeUNL(); @@ -365,7 +368,7 @@ class NegativeUNL_test : public beast::unit_test::suite BEAST_EXPECT(l->negativeUNL().count(publicKeys[1])); BEAST_EXPECT(l->validatorToDisable() == publicKeys[0]); } - l = std::make_shared(*l, env.app().timeKeeper().closeTime()); + l = std::make_shared(*l, env.app().getTimeKeeper().closeTime()); } BEAST_EXPECT(l->isFlagLedger()); l->updateNegativeUNL(); @@ -414,7 +417,7 @@ class NegativeUNL_test : public beast::unit_test::suite BEAST_EXPECT(l->negativeUNL().count(publicKeys[1])); BEAST_EXPECT(l->validatorToReEnable() == publicKeys[0]); } - l = std::make_shared(*l, env.app().timeKeeper().closeTime()); + l = std::make_shared(*l, env.app().getTimeKeeper().closeTime()); } BEAST_EXPECT(l->isFlagLedger()); l->updateNegativeUNL(); @@ -456,7 +459,7 @@ class NegativeUNL_test : public beast::unit_test::suite BEAST_EXPECT(l->negativeUNL().count(publicKeys[1])); BEAST_EXPECT(l->validatorToReEnable() == publicKeys[1]); } - l = std::make_shared(*l, env.app().timeKeeper().closeTime()); + l = std::make_shared(*l, env.app().getTimeKeeper().closeTime()); } BEAST_EXPECT(l->isFlagLedger()); l->updateNegativeUNL(); @@ -470,7 +473,7 @@ class NegativeUNL_test : public beast::unit_test::suite for (auto i = 0; i < 256; ++i) { BEAST_EXPECT(negUnlSizeTest(l, 0, false, false)); - l = std::make_shared(*l, env.app().timeKeeper().closeTime()); + l = std::make_shared(*l, env.app().getTimeKeeper().closeTime()); } BEAST_EXPECT(l->isFlagLedger()); l->updateNegativeUNL(); @@ -543,7 +546,8 @@ struct NetworkHistory static uint256 fake_amendment; // So we have different genesis ledgers auto l = std::make_shared( create_genesis, - env.app().config(), + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), std::vector{fake_amendment++}, env.app().getNodeFamily()); history.push_back(l); @@ -553,7 +557,7 @@ struct NetworkHistory int nidx = 0; while (l->seq() <= param.numLedgers) { - l = std::make_shared(*l, env.app().timeKeeper().closeTime()); + l = std::make_shared(*l, env.app().getTimeKeeper().closeTime()); history.push_back(l); if (l->isFlagLedger()) @@ -601,7 +605,11 @@ struct NetworkHistory { static auto keyPair = randomKeyPair(KeyType::secp256k1); return std::make_shared( - env.app().timeKeeper().now(), keyPair.first, keyPair.second, v, [&](STValidation& v) { + env.app().getTimeKeeper().now(), + keyPair.first, + keyPair.second, + v, + [&](STValidation& v) { v.setFieldH256(sfLedgerHash, ledger->header().hash); v.setFieldU32(sfLedgerSequence, ledger->seq()); v.setFlag(vfFullValidation); @@ -620,7 +628,7 @@ struct NetworkHistory walkHistoryAndAddValidations(NeedValidation&& needVal) { std::uint32_t curr = 0; - std::size_t need = 256 + 1; + std::size_t const need = 256 + 1; // only last 256 + 1 ledgers need validations if (history.size() > need) curr = history.size() - need; @@ -694,14 +702,14 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite testcase("Create UNLModify Tx"); jtx::Env env(*this); - NodeID myId(0xA0); + NodeID const myId(0xA0); NegativeUNLVote vote(myId, env.journal); // one add, one remove auto txSet = std::make_shared(SHAMapType::TRANSACTION, env.app().getNodeFamily()); - PublicKey toDisableKey(derivePublicKey(KeyType::ed25519, randomSecretKey())); - PublicKey toReEnableKey(derivePublicKey(KeyType::ed25519, randomSecretKey())); - LedgerIndex seq(1234); + PublicKey const toDisableKey(derivePublicKey(KeyType::ed25519, randomSecretKey())); + PublicKey const toReEnableKey(derivePublicKey(KeyType::ed25519, randomSecretKey())); + LedgerIndex const seq(1234); BEAST_EXPECT(countTx(txSet) == 0); vote.addTx(seq, toDisableKey, NegativeUNLVote::ToDisable, txSet); BEAST_EXPECT(countTx(txSet) == 1); @@ -715,16 +723,16 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite testPickOneCandidate() { testcase("Pick One Candidate"); - jtx::Env env(*this); + jtx::Env const env(*this); - NodeID myId(0xA0); - NegativeUNLVote vote(myId, env.journal); + NodeID const myId(0xA0); + NegativeUNLVote const vote(myId, env.journal); - uint256 pad_0(0); - uint256 pad_f = ~pad_0; - NodeID n_1(1); - NodeID n_2(2); - NodeID n_3(3); + uint256 const pad_0(0); + uint256 const pad_f = ~pad_0; + NodeID const n_1(1); + NodeID const n_2(2); + NodeID const n_3(3); std::vector candidates({n_1}); BEAST_EXPECT(vote.choose(pad_0, candidates) == n_1); BEAST_EXPECT(vote.choose(pad_f, candidates) == n_1); @@ -782,7 +790,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite 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( @@ -795,7 +803,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite // 5. local node had enough validations but on a wrong chain NetworkHistory history = {*this, {10, 0, false, false, 256 + 2}}; // We need two chains for these tests - bool wrongChainSuccess = history.goodHistory; + bool const wrongChainSuccess = history.goodHistory; BEAST_EXPECT(wrongChainSuccess); NetworkHistory::LedgerHistory wrongChain = std::move(history.history); // Create a new chain and use it as the one that majority of nodes @@ -806,7 +814,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite if (history.goodHistory && wrongChainSuccess) { NodeID myId = history.UNLNodeIDs[3]; - NodeID badNode = history.UNLNodeIDs[4]; + NodeID const badNode = history.UNLNodeIDs[4]; history.walkHistoryAndAddValidations( [&](std::shared_ptr const& l, std::size_t idx) -> bool { // everyone but me @@ -817,9 +825,9 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite // a node double validates for (auto& l : wrongChain) { - RCLValidation v1(history.createSTVal(l, myId)); + RCLValidation const v1(history.createSTVal(l, myId)); history.validations.add(myId, v1); - RCLValidation v2(history.createSTVal(l, badNode)); + RCLValidation const v2(history.createSTVal(l, badNode)); history.validations.add(badNode, v2); } @@ -835,9 +843,13 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite for (auto const& [n, score] : *scoreTable) { if (n == myId) + { BEAST_EXPECT(score == 256); + } else + { BEAST_EXPECT(score == 0); + } } } @@ -886,7 +898,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite * @param numReEnable number of ReEnable candidates expected * @return true if the number of candidates meets expectation */ - bool + static bool checkCandidateSizes( NegativeUNLVote& vote, hash_set const& unl, @@ -897,8 +909,8 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite { auto [disableCandidates, reEnableCandidates] = vote.findAllCandidates(unl, negUnl, scoreTable); - bool rightDisable = disableCandidates.size() == numDisable; - bool rightReEnable = reEnableCandidates.size() == numReEnable; + bool const rightDisable = disableCandidates.size() == numDisable; + bool const rightReEnable = reEnableCandidates.size() == numReEnable; return rightDisable && rightReEnable; }; @@ -997,9 +1009,9 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite { // 2 new validators - NodeID new_1(0xbead); - NodeID new_2(0xbeef); - hash_set nowTrusted = {new_1, new_2}; + NodeID const new_1(0xbead); + NodeID const new_2(0xbeef); + hash_set const nowTrusted = {new_1, new_2}; hash_set UNL_temp = history.UNLNodeIDSet; UNL_temp.insert(new_1); UNL_temp.insert(new_2); @@ -1053,13 +1065,13 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite * negativeUNLMinLocalValsToVote */ - jtx::Env env(*this); + jtx::Env const env(*this); - NodeID myId(0xA0); + NodeID const myId(0xA0); NegativeUNLVote vote(myId, env.journal); - std::array unlSizes = {34, 35, 80}; - std::array nUnlPercent = {0, 50, 100}; + std::array const unlSizes = {34, 35, 80}; + std::array const nUnlPercent = {0, 50, 100}; std::array scores = { 0, NegativeUNLVote::negativeUNLLowWaterMark - 1, @@ -1079,7 +1091,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite hash_set& negUnl, hash_map& scoreTable) { std::vector nodeIDs; - std::vector keys = createPublicKeys(unl_size); + std::vector const keys = createPublicKeys(unl_size); for (auto const& k : keys) { nodeIDs.emplace_back(calcNodeID(k)); @@ -1141,7 +1153,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite hash_set& negUnl, hash_map& scoreTable) { std::vector nodeIDs; - std::vector keys = createPublicKeys(unl_size); + std::vector const keys = createPublicKeys(unl_size); for (auto const& k : keys) { nodeIDs.emplace_back(calcNodeID(k)); @@ -1209,9 +1221,9 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite testNewValidators() { testcase("New Validators"); - jtx::Env env(*this); + jtx::Env const env(*this); - NodeID myId(0xA0); + NodeID const myId(0xA0); NegativeUNLVote vote(myId, env.journal); // test cases: @@ -1220,9 +1232,9 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite // add a new one and some already added // purge and see some are expired - NodeID n1(0xA1); - NodeID n2(0xA2); - NodeID n3(0xA3); + NodeID const n1(0xA1); + NodeID const n2(0xA2); + NodeID const n3(0xA3); vote.newValidators(2, {n1}); BEAST_EXPECT(vote.newValidators_.size() == 1); @@ -1289,7 +1301,7 @@ class NegativeUNLVoteScoreTable_test : public beast::unit_test::suite * -- unl size: 10, 34, 35, 50 * -- score pattern: all 0, all 50%, all 100%, two 0% two 50% rest 100% */ - std::array unlSizes = {10, 34, 35, 50}; + std::array const unlSizes = {10, 34, 35, 50}; std::array, 4> scorePattern = { {{{0, 0, 0}}, {{50, 50, 50}}, {{100, 100, 100}}, {{0, 50, 100}}}}; @@ -1304,17 +1316,23 @@ class NegativeUNLVoteScoreTable_test : public beast::unit_test::suite NodeID myId = history.UNLNodeIDs[3]; history.walkHistoryAndAddValidations( [&](std::shared_ptr const& l, std::size_t idx) -> bool { - std::size_t k; + std::size_t k = 0; if (idx < 2) + { k = 0; + } else if (idx < 4) + { k = 1; + } else + { k = 2; + } - bool add_50 = scorePattern[sp][k] == 50 && l->seq() % 2 == 0; - bool add_100 = scorePattern[sp][k] == 100; - bool add_me = history.UNLNodeIDs[idx] == myId; + bool const add_50 = scorePattern[sp][k] == 50 && l->seq() % 2 == 0; + bool const add_100 = scorePattern[sp][k] == 100; + bool const add_me = history.UNLNodeIDs[idx] == myId; return add_50 || add_100 || add_me; }); @@ -1333,9 +1351,11 @@ class NegativeUNLVoteScoreTable_test : public beast::unit_test::suite if (scorePattern[sp][k] == 50) return score == 256 / 2; if (scorePattern[sp][k] == 100) + { return score == 256; - else - return false; + } + + return false; }; for (; i < 2; ++i) { @@ -1470,6 +1490,7 @@ class NegativeUNLVoteOffline_test : public beast::unit_test::suite if (history.goodHistory) { NodeID n1 = calcNodeID(*history.lastLedger()->negativeUNL().begin()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) NodeID n2 = calcNodeID(*history.lastLedger()->validatorToDisable()); history.walkHistoryAndAddValidations( [&](std::shared_ptr const& l, std::size_t idx) -> bool { @@ -1657,11 +1678,15 @@ class NegativeUNLVoteFilterValidations_test : public beast::unit_test::suite testcase("Filter Validations"); jtx::Env env(*this); auto l = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + Rules{env.app().config().features}, + env.app().config().FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); auto createSTVal = [&](std::pair const& keys) { return std::make_shared( - env.app().timeKeeper().now(), + env.app().getTimeKeeper().now(), keys.first, keys.second, calcNodeID(keys.first), @@ -1673,8 +1698,8 @@ class NegativeUNLVoteFilterValidations_test : public beast::unit_test::suite }; // create keys and validations - std::uint32_t numNodes = 10; - std::uint32_t negUnlSize = 3; + std::uint32_t const numNodes = 10; + std::uint32_t const negUnlSize = 3; std::vector cfgKeys; hash_set activeValidators; hash_set nUnlKeys; @@ -1692,15 +1717,15 @@ class NegativeUNLVoteFilterValidations_test : public beast::unit_test::suite } // setup the ValidatorList - auto& validators = env.app().validators(); + auto& validators = env.app().getValidators(); auto& local = *nUnlKeys.begin(); - std::vector cfgPublishers; + std::vector const cfgPublishers; validators.load(local, cfgKeys, cfgPublishers); validators.updateTrusted( activeValidators, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); BEAST_EXPECT(validators.getTrustedMasterKeys().size() == numNodes); validators.setNegativeUNL(nUnlKeys); @@ -1740,9 +1765,9 @@ negUnlSizeTest( bool hasToDisable, bool hasToReEnable) { - bool sameSize = l->negativeUNL().size() == size; - bool sameToDisable = (l->validatorToDisable() != std::nullopt) == hasToDisable; - bool sameToReEnable = (l->validatorToReEnable() != std::nullopt) == hasToReEnable; + bool const sameSize = l->negativeUNL().size() == size; + bool const sameToDisable = (l->validatorToDisable() != std::nullopt) == hasToDisable; + bool const sameToReEnable = (l->validatorToReEnable() != std::nullopt) == hasToReEnable; return sameSize && sameToDisable && sameToReEnable; } @@ -1752,9 +1777,11 @@ applyAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass) { auto const res = apply(env.app(), view, tx, ApplyFlags::tapNONE, env.journal); if (pass) - return res.ter == tesSUCCESS; - else - return res.ter == tefFAILURE || res.ter == temDISABLED; + { + return isTesSuccess(res.ter); + } + + return res.ter == tefFAILURE || res.ter == temDISABLED; } bool @@ -1782,7 +1809,7 @@ VerifyPubKeyAndSeq( auto s = makeSlice(d); if (!publicKeyType(s)) return false; - PublicKey pk(s); + PublicKey const pk(s); auto it = nUnlLedgerSeq.find(pk); if (it == nUnlLedgerSeq.end()) return false; @@ -1790,7 +1817,7 @@ VerifyPubKeyAndSeq( return false; nUnlLedgerSeq.erase(it); } - return nUnlLedgerSeq.size() == 0; + return nUnlLedgerSeq.empty(); } std::size_t @@ -1808,13 +1835,13 @@ std::vector createPublicKeys(std::size_t n) { std::vector keys; - std::size_t ss = 33; + std::size_t const ss = 33; std::vector data(ss, 0); data[0] = 0xED; for (int i = 0; i < n; ++i) { data[1]++; - Slice s(data.data(), ss); + Slice const s(data.data(), ss); keys.emplace_back(s); } return keys; diff --git a/src/test/consensus/Validations_test.cpp b/src/test/consensus/Validations_test.cpp index f8458e090c..fef6e79036 100644 --- a/src/test/consensus/Validations_test.cpp +++ b/src/test/consensus/Validations_test.cpp @@ -233,12 +233,12 @@ class Validations_test : public beast::unit_test::suite testcase("Add validation"); LedgerHistoryHelper h; - Ledger ledgerA = h["a"]; + Ledger const ledgerA = h["a"]; Ledger ledgerAB = h["ab"]; Ledger ledgerAZ = h["az"]; Ledger ledgerABC = h["abc"]; - Ledger ledgerABCD = h["abcd"]; - Ledger ledgerABCDE = h["abcde"]; + Ledger const ledgerABCD = h["abcd"]; + Ledger const ledgerABCDE = h["abcde"]; { TestHarness harness(h.oracle); @@ -296,7 +296,7 @@ class Validations_test : public beast::unit_test::suite // Process validations out of order with shifted times TestHarness harness(h.oracle); - Node n = harness.makeNode(); + Node const n = harness.makeNode(); // Establish a new current validation BEAST_EXPECT(ValStatus::current == harness.add(n.validate(ledgerA))); @@ -312,7 +312,7 @@ class Validations_test : public beast::unit_test::suite { // Test stale on arrival validations TestHarness harness(h.oracle); - Node n = harness.makeNode(); + Node const n = harness.makeNode(); BEAST_EXPECT( ValStatus::stale == @@ -364,19 +364,19 @@ class Validations_test : public beast::unit_test::suite LedgerHistoryHelper h; Ledger ledgerA = h["a"]; - Ledger ledgerAB = h["ab"]; + Ledger const ledgerAB = h["ab"]; using Trigger = std::function; - std::vector triggers = { + std::vector const triggers = { [&](TestValidations& vals) { vals.currentTrusted(); }, [&](TestValidations& vals) { vals.getCurrentNodeIDs(); }, [&](TestValidations& vals) { vals.getPreferred(genesisLedger); }, [&](TestValidations& vals) { vals.getNodesAfter(ledgerA, ledgerA.id()); }}; - for (Trigger trigger : triggers) + for (Trigger const& trigger : triggers) { TestHarness harness(h.oracle); - Node n = harness.makeNode(); + Node const n = harness.makeNode(); BEAST_EXPECT(ValStatus::current == harness.add(n.validate(ledgerAB))); trigger(harness.vals()); @@ -405,39 +405,41 @@ class Validations_test : public beast::unit_test::suite testcase("Get nodes after"); LedgerHistoryHelper h; - Ledger ledgerA = h["a"]; - Ledger ledgerAB = h["ab"]; - Ledger ledgerABC = h["abc"]; - Ledger ledgerAD = h["ad"]; + Ledger const ledgerA = h["a"]; + Ledger const ledgerAB = h["ab"]; + Ledger const ledgerABC = h["abc"]; + Ledger const ledgerAD = h["ad"]; TestHarness harness(h.oracle); - Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(), - d = harness.makeNode(); - c.untrust(); + Node const trustedNode1 = harness.makeNode(); + Node const trustedNode2 = harness.makeNode(); + Node const trustedNode3 = harness.makeNode(); + + Node notTrustedNode = harness.makeNode(); + notTrustedNode.untrust(); // first round a,b,c agree, d has is partial - BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA))); - BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerA))); - BEAST_EXPECT(ValStatus::current == harness.add(c.validate(ledgerA))); - BEAST_EXPECT(ValStatus::current == harness.add(d.partial(ledgerA))); + BEAST_EXPECT(ValStatus::current == harness.add(trustedNode1.validate(ledgerA))); + BEAST_EXPECT(ValStatus::current == harness.add(trustedNode2.validate(ledgerA))); + BEAST_EXPECT(ValStatus::current == harness.add(notTrustedNode.validate(ledgerA))); + BEAST_EXPECT(ValStatus::current == harness.add(trustedNode3.partial(ledgerA))); for (Ledger const& ledger : {ledgerA, ledgerAB, ledgerABC, ledgerAD}) BEAST_EXPECT(harness.vals().getNodesAfter(ledger, ledger.id()) == 0); harness.clock().advance(5s); - BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerAB))); - BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerABC))); - BEAST_EXPECT(ValStatus::current == harness.add(c.validate(ledgerAB))); - BEAST_EXPECT(ValStatus::current == harness.add(d.partial(ledgerABC))); + BEAST_EXPECT(ValStatus::current == harness.add(trustedNode1.validate(ledgerAB))); + BEAST_EXPECT(ValStatus::current == harness.add(trustedNode2.validate(ledgerABC))); + BEAST_EXPECT(ValStatus::current == harness.add(notTrustedNode.validate(ledgerAB))); + BEAST_EXPECT(ValStatus::current == harness.add(trustedNode3.partial(ledgerABC))); BEAST_EXPECT(harness.vals().getNodesAfter(ledgerA, ledgerA.id()) == 3); BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAB, ledgerAB.id()) == 2); BEAST_EXPECT(harness.vals().getNodesAfter(ledgerABC, ledgerABC.id()) == 0); BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAD, ledgerAD.id()) == 0); - // If given a ledger inconsistent with the id, is still able to check - // using slower method + // If given a ledger inconsistent with the id, is still able to check using slower method BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAD, ledgerA.id()) == 1); BEAST_EXPECT(harness.vals().getNodesAfter(ledgerAD, ledgerAB.id()) == 2); } @@ -449,12 +451,13 @@ class Validations_test : public beast::unit_test::suite testcase("Current trusted validations"); LedgerHistoryHelper h; - Ledger ledgerA = h["a"]; - Ledger ledgerB = h["b"]; - Ledger ledgerAC = h["ac"]; + Ledger const ledgerA = h["a"]; + Ledger const ledgerB = h["b"]; + Ledger const ledgerAC = h["ac"]; TestHarness harness(h.oracle); - Node a = harness.makeNode(), b = harness.makeNode(); + Node const a = harness.makeNode(); + Node b = harness.makeNode(); b.untrust(); BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerA))); @@ -487,8 +490,8 @@ class Validations_test : public beast::unit_test::suite testcase("Current public keys"); LedgerHistoryHelper h; - Ledger ledgerA = h["a"]; - Ledger ledgerAC = h["ac"]; + Ledger const ledgerA = h["a"]; + Ledger const ledgerAC = h["ac"]; TestHarness harness(h.oracle); Node a = harness.makeNode(), b = harness.makeNode(); @@ -567,8 +570,9 @@ class Validations_test : public beast::unit_test::suite sorted(harness.vals().getTrustedForLedger(id, seq)) == sorted(expectedValidations)); - std::uint32_t baseFee = 0; + std::uint32_t const baseFee = 0; std::vector expectedFees; + expectedFees.reserve(expectedValidations.size()); for (auto const& val : expectedValidations) { expectedFees.push_back(val.loadFee().value_or(baseFee)); @@ -579,9 +583,9 @@ class Validations_test : public beast::unit_test::suite }; //---------------------------------------------------------------------- - Ledger ledgerA = h["a"]; - Ledger ledgerB = h["b"]; - Ledger ledgerAC = h["ac"]; + Ledger const ledgerA = h["a"]; + Ledger const ledgerB = h["b"]; + Ledger const ledgerAC = h["ac"]; // Add a dummy ID to cover unknown ledger identifiers trustedValidations[{Ledger::ID{100}, Ledger::Seq{100}}] = {}; @@ -688,14 +692,16 @@ class Validations_test : public beast::unit_test::suite LedgerHistoryHelper h; TestHarness harness(h.oracle); - Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(); - c.untrust(); + Node const trustedNode1 = harness.makeNode(); + Node const trustedNode2 = harness.makeNode(); + Node notTrustedNode = harness.makeNode(); + notTrustedNode.untrust(); - Ledger ledgerA = h["a"]; - Ledger ledgerAB = h["ab"]; + Ledger const ledgerA = h["a"]; + Ledger const ledgerAB = h["ab"]; hash_map expected; - for (auto const& node : {a, b, c}) + for (auto const& node : {trustedNode1, trustedNode2, notTrustedNode}) { auto const val = node.validate(ledgerA); BEAST_EXPECT(ValStatus::current == harness.add(val)); @@ -705,9 +711,9 @@ class Validations_test : public beast::unit_test::suite // Send in a new validation for a, saving the new one into the expected // map after setting the proper prior ledger ID it replaced harness.clock().advance(1s); - auto newVal = a.validate(ledgerAB); + auto newVal = trustedNode1.validate(ledgerAB); BEAST_EXPECT(ValStatus::current == harness.add(newVal)); - expected.find(a.nodeID())->second = newVal; + expected.find(trustedNode1.nodeID())->second = newVal; } void @@ -718,14 +724,17 @@ class Validations_test : public beast::unit_test::suite LedgerHistoryHelper h; TestHarness harness(h.oracle); - Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(), - d = harness.makeNode(); - c.untrust(); + Node const trustedNode1 = harness.makeNode(); + Node const trustedNode2 = harness.makeNode(); + Node const trustedNode3 = harness.makeNode(); - Ledger ledgerA = h["a"]; - Ledger ledgerB = h["b"]; - Ledger ledgerAC = h["ac"]; - Ledger ledgerACD = h["acd"]; + Node notTrustedNode = harness.makeNode(); + notTrustedNode.untrust(); + + Ledger const ledgerA = h["a"]; + Ledger const ledgerB = h["b"]; + Ledger const ledgerAC = h["ac"]; + Ledger const ledgerACD = h["acd"]; using Seq = Ledger::Seq; @@ -735,7 +744,7 @@ class Validations_test : public beast::unit_test::suite BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == std::nullopt); // Single ledger - BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerB))); + BEAST_EXPECT(ValStatus::current == harness.add(trustedNode1.validate(ledgerB))); BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerB)); BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerB)); @@ -744,21 +753,21 @@ class Validations_test : public beast::unit_test::suite // Untrusted doesn't impact preferred ledger // (ledgerB has tie-break over ledgerA) - BEAST_EXPECT(ValStatus::current == harness.add(b.validate(ledgerA))); - BEAST_EXPECT(ValStatus::current == harness.add(c.validate(ledgerA))); + BEAST_EXPECT(ValStatus::current == harness.add(trustedNode2.validate(ledgerA))); + BEAST_EXPECT(ValStatus::current == harness.add(notTrustedNode.validate(ledgerA))); BEAST_EXPECT(ledgerB.id() > ledgerA.id()); BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerB)); BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerB)); // Partial does break ties - BEAST_EXPECT(ValStatus::current == harness.add(d.partial(ledgerA))); + BEAST_EXPECT(ValStatus::current == harness.add(trustedNode3.partial(ledgerA))); BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerA)); BEAST_EXPECT(harness.vals().getPreferred(ledgerB) == pref(ledgerA)); harness.clock().advance(5s); // Parent of preferred-> stick with ledger - for (auto const& node : {a, b, c, d}) + for (auto const& node : {trustedNode1, trustedNode2, notTrustedNode, trustedNode3}) BEAST_EXPECT(ValStatus::current == harness.add(node.validate(ledgerAC))); // Parent of preferred stays put BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == pref(ledgerA)); @@ -769,7 +778,7 @@ class Validations_test : public beast::unit_test::suite // Any later grandchild or different chain is preferred harness.clock().advance(5s); - for (auto const& node : {a, b, c, d}) + for (auto const& node : {trustedNode1, trustedNode2, notTrustedNode, trustedNode3}) BEAST_EXPECT(ValStatus::current == harness.add(node.validate(ledgerACD))); for (auto const& ledger : {ledgerA, ledgerB, ledgerACD}) BEAST_EXPECT(harness.vals().getPreferred(ledger) == pref(ledgerACD)); @@ -783,11 +792,11 @@ class Validations_test : public beast::unit_test::suite LedgerHistoryHelper h; TestHarness harness(h.oracle); - Node a = harness.makeNode(); + Node const a = harness.makeNode(); - Ledger ledgerA = h["a"]; - Ledger ledgerB = h["b"]; - Ledger ledgerC = h["c"]; + Ledger const ledgerA = h["a"]; + Ledger const ledgerB = h["b"]; + Ledger const ledgerC = h["c"]; using ID = Ledger::ID; using Seq = Ledger::Seq; @@ -829,14 +838,14 @@ class Validations_test : public beast::unit_test::suite LedgerHistoryHelper h; TestHarness harness(h.oracle); - Node a = harness.makeNode(); - Node b = harness.makeNode(); + Node const a = harness.makeNode(); + Node const b = harness.makeNode(); using ID = Ledger::ID; using Seq = Ledger::Seq; // Validate the ledger before it is actually available - Validation val = a.validate(ID{2}, Seq{2}, 0s, 0s, true); + Validation const val = a.validate(ID{2}, Seq{2}, 0s, 0s, true); BEAST_EXPECT(ValStatus::current == harness.add(val)); // Validation is available @@ -853,13 +862,13 @@ class Validations_test : public beast::unit_test::suite BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::make_pair(Seq{2}, ID{3})); // Create the ledger - Ledger ledgerAB = h["ab"]; + Ledger const ledgerAB = h["ab"]; // Now it should be available BEAST_EXPECT(harness.vals().getNodesAfter(genesisLedger, ID{0}) == 1); // Create a validation that is not available harness.clock().advance(5s); - Validation val2 = a.validate(ID{4}, Seq{4}, 0s, 0s, true); + Validation const val2 = a.validate(ID{4}, Seq{4}, 0s, 0s, true); BEAST_EXPECT(ValStatus::current == harness.add(val2)); BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{4}) == 1); BEAST_EXPECT( @@ -867,7 +876,7 @@ class Validations_test : public beast::unit_test::suite std::make_pair(ledgerAB.seq(), ledgerAB.id())); // Another node requesting that ledger still doesn't change things - Validation val3 = b.validate(ID{4}, Seq{4}, 0s, 0s, true); + Validation const val3 = b.validate(ID{4}, Seq{4}, 0s, 0s, true); BEAST_EXPECT(ValStatus::current == harness.add(val3)); BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{4}) == 2); BEAST_EXPECT( @@ -876,7 +885,7 @@ class Validations_test : public beast::unit_test::suite // Switch to validation that is available harness.clock().advance(5s); - Ledger ledgerABCDE = h["abcde"]; + Ledger const ledgerABCDE = h["abcde"]; BEAST_EXPECT(ValStatus::current == harness.add(a.partial(ledgerABCDE))); BEAST_EXPECT(ValStatus::current == harness.add(b.partial(ledgerABCDE))); BEAST_EXPECT( @@ -890,9 +899,9 @@ class Validations_test : public beast::unit_test::suite testcase("NumTrustedForLedger"); LedgerHistoryHelper h; TestHarness harness(h.oracle); - Node a = harness.makeNode(); - Node b = harness.makeNode(); - Ledger ledgerA = h["a"]; + Node const a = harness.makeNode(); + Node const b = harness.makeNode(); + Ledger const ledgerA = h["a"]; BEAST_EXPECT(ValStatus::current == harness.add(a.partial(ledgerA))); BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 0); @@ -911,7 +920,7 @@ class Validations_test : public beast::unit_test::suite beast::manual_clock clock; SeqEnforcer enforcer; - ValidationParms p; + ValidationParms const p; BEAST_EXPECT(enforcer(clock.now(), Seq{1}, p)); BEAST_EXPECT(enforcer(clock.now(), Seq{10}, p)); @@ -933,18 +942,22 @@ class Validations_test : public beast::unit_test::suite TestValidations& vals, hash_set const& listed, std::vector const& trustedVals) { - Ledger::ID testID = + Ledger::ID const testID = trustedVals.empty() ? this->genesisLedger.id() : trustedVals[0].ledgerID(); - Ledger::Seq testSeq = + Ledger::Seq const testSeq = trustedVals.empty() ? this->genesisLedger.seq() : trustedVals[0].seq(); BEAST_EXPECT(vals.currentTrusted() == trustedVals); BEAST_EXPECT(vals.getCurrentNodeIDs() == listed); BEAST_EXPECT( vals.getNodesAfter(this->genesisLedger, genesisLedger.id()) == trustedVals.size()); if (trustedVals.empty()) + { BEAST_EXPECT(vals.getPreferred(this->genesisLedger) == std::nullopt); + } else + { BEAST_EXPECT(vals.getPreferred(this->genesisLedger)->second == testID); + } BEAST_EXPECT(vals.getTrustedForLedger(testID, testSeq) == trustedVals); BEAST_EXPECT(vals.numTrustedForLedger(testID) == trustedVals.size()); }; @@ -953,12 +966,12 @@ class Validations_test : public beast::unit_test::suite // Trusted to untrusted LedgerHistoryHelper h; TestHarness harness(h.oracle); - Node a = harness.makeNode(); - Ledger ledgerAB = h["ab"]; - Validation v = a.validate(ledgerAB); + Node const a = harness.makeNode(); + Ledger const ledgerAB = h["ab"]; + Validation const v = a.validate(ledgerAB); BEAST_EXPECT(ValStatus::current == harness.add(v)); - hash_set listed({a.nodeID()}); + hash_set const listed({a.nodeID()}); std::vector trustedVals({v}); checker(harness.vals(), listed, trustedVals); @@ -973,11 +986,11 @@ class Validations_test : public beast::unit_test::suite TestHarness harness(h.oracle); Node a = harness.makeNode(); a.untrust(); - Ledger ledgerAB = h["ab"]; - Validation v = a.validate(ledgerAB); + Ledger const ledgerAB = h["ab"]; + Validation const v = a.validate(ledgerAB); BEAST_EXPECT(ValStatus::current == harness.add(v)); - hash_set listed({a.nodeID()}); + hash_set const listed({a.nodeID()}); std::vector trustedVals; checker(harness.vals(), listed, trustedVals); @@ -990,14 +1003,16 @@ class Validations_test : public beast::unit_test::suite // Trusted but not acquired -> untrusted LedgerHistoryHelper h; TestHarness harness(h.oracle); - Node a = harness.makeNode(); - Validation v = a.validate(Ledger::ID{2}, Ledger::Seq{2}, 0s, 0s, true); + Node const a = harness.makeNode(); + Validation const v = a.validate(Ledger::ID{2}, Ledger::Seq{2}, 0s, 0s, true); BEAST_EXPECT(ValStatus::current == harness.add(v)); - hash_set listed({a.nodeID()}); + hash_set const listed({a.nodeID()}); std::vector trustedVals({v}); auto& vals = harness.vals(); BEAST_EXPECT(vals.currentTrusted() == trustedVals); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(vals.getPreferred(genesisLedger)->second == v.ledgerID()); BEAST_EXPECT(vals.getNodesAfter(genesisLedger, genesisLedger.id()) == 0); diff --git a/src/test/core/ClosureCounter_test.cpp b/src/test/core/ClosureCounter_test.cpp index 5cce837fb0..088b7b9ff9 100644 --- a/src/test/core/ClosureCounter_test.cpp +++ b/src/test/core/ClosureCounter_test.cpp @@ -17,7 +17,7 @@ class ClosureCounter_test : public beast::unit_test::suite // We're only using Env for its Journal. That Journal gives better // coverage in unit tests. test::jtx::Env env_{*this, jtx::envconfig(), nullptr, beast::severities::kDisabled}; - beast::Journal j{env_.app().journal("ClosureCounter_test")}; + beast::Journal j{env_.app().getJournal("ClosureCounter_test")}; void testConstruction() @@ -36,9 +36,9 @@ class ClosureCounter_test : public beast::unit_test::suite BEAST_EXPECT(wrapped); // wrapped() should be callable with no arguments. - (*wrapped)(); + (*wrapped)(); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(evidence == 1); - (*wrapped)(); + (*wrapped)(); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(evidence == 2); // Destroying the contents of wrapped should decrement voidCounter. @@ -60,9 +60,9 @@ class ClosureCounter_test : public beast::unit_test::suite BEAST_EXPECT(wrapped); // wrapped() should be callable with one integer argument. - (*wrapped)(5); + (*wrapped)(5); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(evidence == 5); - (*wrapped)(11); + (*wrapped)(11); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(evidence == 11); // Destroying the contents of wrapped should decrement setCounter. @@ -82,8 +82,8 @@ class ClosureCounter_test : public beast::unit_test::suite BEAST_EXPECT(wrapped); // wrapped() should be callable with two integers. - BEAST_EXPECT((*wrapped)(5, 2) == 7); - BEAST_EXPECT((*wrapped)(2, -8) == -6); + BEAST_EXPECT((*wrapped)(5, 2) == 7); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT((*wrapped)(2, -8) == -6); // NOLINT(bugprone-unchecked-optional-access) // Destroying the contents of wrapped should decrement sumCounter. wrapped = std::nullopt; @@ -154,7 +154,8 @@ class ClosureCounter_test : public beast::unit_test::suite BEAST_EXPECT(wrapped); TrackedString const strValue("value"); - TrackedString const result = (*wrapped)(strValue); + TrackedString const result = + (*wrapped)(strValue); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(result.copies == 2); BEAST_EXPECT(result.moves == 1); BEAST_EXPECT(result.str == "value!"); @@ -171,7 +172,8 @@ class ClosureCounter_test : public beast::unit_test::suite BEAST_EXPECT(wrapped); TrackedString const strConstLValue("const lvalue"); - TrackedString const result = (*wrapped)(strConstLValue); + TrackedString const result = + (*wrapped)(strConstLValue); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(result.copies == 1); // BEAST_EXPECT (result.moves == ?); // moves VS == 1, gcc == 0 BEAST_EXPECT(result.str == "const lvalue!"); @@ -188,7 +190,8 @@ class ClosureCounter_test : public beast::unit_test::suite BEAST_EXPECT(wrapped); TrackedString strLValue("lvalue"); - TrackedString const result = (*wrapped)(strLValue); + TrackedString const result = + (*wrapped)(strLValue); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(result.copies == 1); BEAST_EXPECT(result.moves == 0); BEAST_EXPECT(result.str == "lvalue!"); @@ -204,20 +207,21 @@ class ClosureCounter_test : public beast::unit_test::suite // leaving scope. So, without intervention, they would // do a copy for the return (June 2017). An explicit // std::move() was required. - return std::move(in += "!"); + in += "!"; + return std::move(in); }); BEAST_EXPECT(strCounter.count() == 1); BEAST_EXPECT(wrapped); - // Make the string big enough to (probably) avoid the small string - // optimization. + // Make the string big enough to (probably) avoid the small string optimization. TrackedString strRValue("rvalue abcdefghijklmnopqrstuvwxyz"); - TrackedString const result = (*wrapped)(std::move(strRValue)); + TrackedString const result = + (*wrapped)(std::move(strRValue)); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(result.copies == 0); BEAST_EXPECT(result.moves == 1); BEAST_EXPECT(result.str == "rvalue abcdefghijklmnopqrstuvwxyz!"); - BEAST_EXPECT(strRValue.str.size() == 0); + BEAST_EXPECT(strRValue.str.empty()); // NOLINT(bugprone-use-after-move) } } @@ -295,7 +299,7 @@ class ClosureCounter_test : public beast::unit_test::suite BEAST_EXPECT(voidCounter.count() == 0); // Wait for the thread to exit. - while (threadExited == false) + while (!threadExited) ; localThread.join(); } diff --git a/src/test/core/Config_test.cpp b/src/test/core/Config_test.cpp index dd6e0377d5..6392a75f80 100644 --- a/src/test/core/Config_test.cpp +++ b/src/test/core/Config_test.cpp @@ -81,7 +81,7 @@ time.apple.com time.nist.gov pool.ntp.org -# Where to find some other servers speaking the Ripple protocol. +# Where to find some other servers speaking the XRPL protocol. # [ips] r.ripple.com 51235 @@ -107,7 +107,7 @@ backend=sqlite } /** - Write a xrpld config file and remove when done. + Write an xrpld config file and remove when done. */ class FileCfgGuard : public xrpl::detail::FileDirGuard { @@ -268,7 +268,7 @@ public: Config c; - std::string toLoad(R"xrpldConfig( + std::string const toLoad(R"xrpldConfig( [server] port_rpc port_peer @@ -284,7 +284,7 @@ port_wss_admin expectException([&c] { c.legacy("server"); }); // not a single line // set a legacy value - BEAST_EXPECT(c.legacy("not_in_file") == ""); + BEAST_EXPECT(c.legacy("not_in_file").empty()); c.legacy("not_in_file", "new_value"); BEAST_EXPECT(c.legacy("not_in_file") == "new_value"); } @@ -297,15 +297,15 @@ port_wss_admin auto const cwd = current_path(); // Test both config file names. - char const* configFiles[] = {Config::configFileName, Config::configLegacyName}; + std::string_view const configFiles[] = {Config::configFileName, Config::configLegacyName}; // Config file in current directory. for (auto const& configFile : configFiles) { // Use a temporary directory for testing. - beast::temp_dir td; + beast::temp_dir const td; current_path(td.path()); - path const f = td.file(configFile); + path const f = td.file(std::string{configFile}); std::ofstream o(f.string()); o << detail::configContents("", ""); o.close(); @@ -325,13 +325,13 @@ port_wss_admin { // Point the current working directory to a temporary directory, so // we don't pick up an actual config file from the repository root. - beast::temp_dir td; + beast::temp_dir const td; current_path(td.path()); // The XDG config directory is set: the config file must be in a // subdirectory named after the system. { - beast::temp_dir tc; + beast::temp_dir const tc; // Set the HOME and XDG_CONFIG_HOME environment variables. The // HOME variable is not used when XDG_CONFIG_HOME is set, but @@ -344,7 +344,7 @@ port_wss_admin // Create the config file in '${XDG_CONFIG_HOME}/[systemName]'. path p = tc.file(systemName()); create_directory(p); - p = tc.file(systemName() + "/" + configFile); + p = tc.file(systemName() + "/" + std::string{configFile}); std::ofstream o(p.string()); o << detail::configContents("", ""); o.close(); @@ -358,14 +358,14 @@ port_wss_admin "/Users/dummy/xrpld/config/log/debug.log"); // Restore the environment variables. - h ? setenv("HOME", h, 1) : unsetenv("HOME"); - x ? setenv("XDG_CONFIG_HOME", x, 1) : unsetenv("XDG_CONFIG_HOME"); + (h != nullptr) ? setenv("HOME", h, 1) : unsetenv("HOME"); + (x != nullptr) ? setenv("XDG_CONFIG_HOME", x, 1) : unsetenv("XDG_CONFIG_HOME"); } // The XDG config directory is not set: the config file must be in a // subdirectory named .config followed by the system name. { - beast::temp_dir tc; + beast::temp_dir const tc; // Set only the HOME environment variable. char const* h = getenv("HOME"); @@ -380,7 +380,7 @@ port_wss_admin s += "/" + systemName(); p = tc.file(s); create_directory(p); - p = tc.file(s + "/" + configFile); + p = tc.file(s + "/" + std::string{configFile}); std::ofstream o(p.string()); o << detail::configContents("", ""); o.close(); @@ -394,8 +394,8 @@ port_wss_admin "/Users/dummy/xrpld/config/log/debug.log"); // Restore the environment variables. - h ? setenv("HOME", h, 1) : unsetenv("HOME"); - if (x) + (h != nullptr) ? setenv("HOME", h, 1) : unsetenv("HOME"); + if (x != nullptr) setenv("XDG_CONFIG_HOME", x, 1); } } @@ -434,7 +434,7 @@ port_wss_admin // load will not. Config c; c.loadFromString(""); - BEAST_EXPECT(c.legacy("database_path") == ""); + BEAST_EXPECT(c.legacy("database_path").empty()); } } { @@ -533,7 +533,7 @@ main error = e.what(); } - BEAST_EXPECT(error == ""); + BEAST_EXPECT(error.empty()); BEAST_EXPECT(c.NETWORK_ID == 0); try @@ -546,7 +546,7 @@ main error = e.what(); } - BEAST_EXPECT(error == ""); + BEAST_EXPECT(error.empty()); BEAST_EXPECT(c.NETWORK_ID == 0); try @@ -561,7 +561,7 @@ main error = e.what(); } - BEAST_EXPECT(error == ""); + BEAST_EXPECT(error.empty()); BEAST_EXPECT(c.NETWORK_ID == 255); try @@ -576,7 +576,7 @@ main error = e.what(); } - BEAST_EXPECT(error == ""); + BEAST_EXPECT(error.empty()); BEAST_EXPECT(c.NETWORK_ID == 10000); } @@ -626,7 +626,7 @@ main { // load validators from config into single section Config c; - std::string toLoad(R"xrpldConfig( + std::string const toLoad(R"xrpldConfig( [validators] n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7 n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj @@ -644,7 +644,7 @@ nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8 { // load validator list sites and keys from config Config c; - std::string toLoad(R"xrpldConfig( + std::string const toLoad(R"xrpldConfig( [validator_list_sites] xrpl-validators.com trust-these-validators.gov @@ -674,7 +674,7 @@ trust-these-validators.gov { // load validator list sites and keys from config Config c; - std::string toLoad(R"xrpldConfig( + std::string const toLoad(R"xrpldConfig( [validator_list_sites] xrpl-validators.com trust-these-validators.gov @@ -705,7 +705,7 @@ trust-these-validators.gov // load should throw if [validator_list_threshold] is greater than // the number of [validator_list_keys] Config c; - std::string toLoad(R"xrpldConfig( + std::string const toLoad(R"xrpldConfig( [validator_list_sites] xrpl-validators.com trust-these-validators.gov @@ -734,7 +734,7 @@ trust-these-validators.gov { // load should throw if [validator_list_threshold] is malformed Config c; - std::string toLoad(R"xrpldConfig( + std::string const toLoad(R"xrpldConfig( [validator_list_sites] xrpl-validators.com trust-these-validators.gov @@ -763,7 +763,7 @@ value = 2 { // load should throw if [validator_list_threshold] is negative Config c; - std::string toLoad(R"xrpldConfig( + std::string const toLoad(R"xrpldConfig( [validator_list_sites] xrpl-validators.com trust-these-validators.gov @@ -790,7 +790,7 @@ trust-these-validators.gov // load should throw if [validator_list_sites] is configured but // [validator_list_keys] is not Config c; - std::string toLoad(R"xrpldConfig( + std::string const toLoad(R"xrpldConfig( [validator_list_sites] xrpl-validators.com trust-these-validators.gov @@ -958,7 +958,7 @@ trust-these-validators.gov // load should throw if [validators], [validator_keys] and // [validator_list_keys] are missing from xrpld.cfg and // validators file - Config c; + Config const c; boost::format cc("[validators_file]\n%1%\n"); std::string error; detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.cfg"); @@ -968,7 +968,7 @@ trust-these-validators.gov "[validators], [validator_keys] or [validator_list_keys] " "section: " + vtg.validatorsFile(); - std::ofstream o(vtg.validatorsFile()); + std::ofstream const o(vtg.validatorsFile()); try { Config c2; @@ -1141,7 +1141,7 @@ trust-these-validators.gov Config cfg; /* NOTE: this string includes some explicit * space chars in order to verify proper trimming */ - std::string toLoad( + std::string const toLoad( R"( [port_rpc])" "\x20" @@ -1182,7 +1182,7 @@ r.ripple.com 51235 Config cfg; /* NOTE: this string includes some explicit * space chars in order to verify proper trimming */ - std::string toLoad( + std::string const toLoad( R"( [port_rpc])" "\x20" @@ -1261,7 +1261,7 @@ r.ripple.com:51235 bool had_comment; }; - std::array tests = { + std::array const tests = { {{"password = aaaa\\#bbbb", "password", "aaaa#bbbb", false}, {"password = aaaa#bbbb", "password", "aaaa", true}, {"password = aaaa #bbbb", "password", "aaaa", true}, @@ -1299,6 +1299,7 @@ r.ripple.com:51235 s.append("online_delete = 3000"); std::uint32_t od = 0; BEAST_EXPECT(set(od, "online_delete", s)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECTS(od == 3000, *(s.get("online_delete"))); } @@ -1307,6 +1308,7 @@ r.ripple.com:51235 s.append("online_delete = 2000 #my comment on this"); std::uint32_t od = 0; BEAST_EXPECT(set(od, "online_delete", s)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECTS(od == 2000, *(s.get("online_delete"))); } } @@ -1335,7 +1337,7 @@ r.ripple.com:51235 auto val_3 = get(s, "a_string"); BEAST_EXPECT(val_3 == "mystring"); auto val_4 = get(s, "not_a_key"); - BEAST_EXPECT(val_4 == ""); + BEAST_EXPECT(val_4.empty()); auto val_5 = get(s, "not_a_key", "default"); BEAST_EXPECT(val_5 == "default"); @@ -1417,7 +1419,7 @@ r.ripple.com:51235 bool shouldPass; }; - std::vector units = { + std::vector const units = { {"seconds", 1, 15 * 60, false}, {"minutes", 60, 14, false}, {"minutes", 60, 15, true}, @@ -1427,7 +1429,7 @@ r.ripple.com:51235 {"months", 2592000, 1, false}, {"years", 31536000, 1, false}}; - std::string space = ""; + std::string space; for (auto& [unit, sec, val, shouldPass] : units) { Config c; @@ -1435,22 +1437,30 @@ r.ripple.com:51235 [amendment_majority_time] )xrpldConfig"); toLoad += std::to_string(val) + space + unit; - space = space == "" ? " " : ""; + space = space.empty() ? " " : ""; try { c.loadFromString(toLoad); if (shouldPass) + { BEAST_EXPECT(c.AMENDMENT_MAJORITY_TIME.count() == val * sec); + } else + { fail(); + } } catch (std::runtime_error&) { if (!shouldPass) + { pass(); + } else + { fail(); + } } } } diff --git a/src/test/core/Coroutine_test.cpp b/src/test/core/Coroutine_test.cpp index 73dc3c2f8f..4cfb86f931 100644 --- a/src/test/core/Coroutine_test.cpp +++ b/src/test/core/Coroutine_test.cpp @@ -34,7 +34,7 @@ public: void signal() { - std::lock_guard lk(mutex_); + std::lock_guard const lk(mutex_); signaled_ = true; cv_.notify_all(); } diff --git a/src/test/core/JobQueue_test.cpp b/src/test/core/JobQueue_test.cpp index 8289e305a0..13142c299f 100644 --- a/src/test/core/JobQueue_test.cpp +++ b/src/test/core/JobQueue_test.cpp @@ -23,7 +23,7 @@ class JobQueue_test : public beast::unit_test::suite jQueue.addJob(jtCLIENT, "JobAddTest1", [&jobRan]() { jobRan = true; }) == true); // Wait for the Job to run. - while (jobRan == false) + while (!jobRan) ; } { @@ -36,7 +36,7 @@ class JobQueue_test : public beast::unit_test::suite // The Job should never run, so having the Job access this // unprotected variable on the stack should be completely safe. // Not recommended for the faint of heart... - bool unprotected; + bool unprotected = false; BEAST_EXPECT(jQueue.addJob(jtCLIENT, "JobAddTest2", [&unprotected]() { unprotected = false; }) == false); @@ -118,7 +118,7 @@ class JobQueue_test : public beast::unit_test::suite // The Coro should never run, so having the Coro access this // unprotected variable on the stack should be completely safe. // Not recommended for the faint of heart... - bool unprotected; + bool unprotected = false; auto const coro = jQueue.postCoro( jtCLIENT, "PostCoroTest3", [&unprotected](std::shared_ptr const&) { unprotected = false; diff --git a/src/test/core/SociDB_test.cpp b/src/test/core/SociDB_test.cpp index 66b368176d..c58c34756a 100644 --- a/src/test/core/SociDB_test.cpp +++ b/src/test/core/SociDB_test.cpp @@ -7,6 +7,8 @@ #include #include +#include + namespace xrpl { class SociDB_test final : public TestSuite { @@ -87,7 +89,7 @@ public: for (auto const& i : d) { - DBConfig sc(c, i.first); + DBConfig const sc(c, i.first); BEAST_EXPECT(boost::ends_with(sc.connectionString(), i.first + i.second)); } } @@ -97,7 +99,7 @@ public: testcase("open"); BasicConfig c; setupSQLiteConfig(c, getDatabasePath()); - DBConfig sc(c, "SociTestDB"); + DBConfig const sc(c, "SociTestDB"); std::vector const stringData({"String1", "String2", "String3"}); std::vector const intData({1, 2, 3}); auto checkValues = [this, &stringData, &intData](soci::session& s) { @@ -142,7 +144,7 @@ public: { namespace bfs = boost::filesystem; // Remove the database - bfs::path dbPath(sc.connectionString()); + bfs::path const dbPath(sc.connectionString()); if (bfs::is_regular_file(dbPath)) bfs::remove(dbPath); } @@ -154,7 +156,7 @@ public: testcase("select"); BasicConfig c; setupSQLiteConfig(c, getDatabasePath()); - DBConfig sc(c, "SociTestDB"); + DBConfig const sc(c, "SociTestDB"); std::vector const ubid( {(std::uint64_t)std::numeric_limits::max(), 20, 30}); std::vector const bid({-10, -20, -30}); @@ -272,7 +274,7 @@ public: { namespace bfs = boost::filesystem; // Remove the database - bfs::path dbPath(sc.connectionString()); + bfs::path const dbPath(sc.connectionString()); if (bfs::is_regular_file(dbPath)) bfs::remove(dbPath); } @@ -283,11 +285,12 @@ public: testcase("deleteWithSubselect"); BasicConfig c; setupSQLiteConfig(c, getDatabasePath()); - DBConfig sc(c, "SociTestDB"); + DBConfig const sc(c, "SociTestDB"); { soci::session s; sc.open(s); - char const* dbInit[] = { + + std::string_view const dbInit[] = { "BEGIN TRANSACTION;", "CREATE TABLE Ledgers ( \ LedgerHash CHARACTER(64) PRIMARY KEY, \ @@ -323,7 +326,7 @@ public: } namespace bfs = boost::filesystem; // Remove the database - bfs::path dbPath(sc.connectionString()); + bfs::path const dbPath(sc.connectionString()); if (bfs::is_regular_file(dbPath)) bfs::remove(dbPath); } diff --git a/src/test/core/Workers_test.cpp b/src/test/core/Workers_test.cpp index 65245b8c94..6631cff1c4 100644 --- a/src/test/core/Workers_test.cpp +++ b/src/test/core/Workers_test.cpp @@ -88,7 +88,7 @@ public: void processTask(int instance) override { - std::lock_guard lk{mut}; + std::lock_guard const lk{mut}; if (--count == 0) cv.notify_all(); } @@ -106,7 +106,7 @@ public: std::to_string(tc3)); TestCallback cb; - std::unique_ptr perfLog = std::make_unique(); + std::unique_ptr const perfLog = std::make_unique(); Workers w(cb, perfLog.get(), "Test", tc1); BEAST_EXPECT(w.getNumberOfThreads() == tc1); diff --git a/src/test/csf/BasicNetwork.h b/src/test/csf/BasicNetwork.h index 697b20c2c7..85c77ac47d 100644 --- a/src/test/csf/BasicNetwork.h +++ b/src/test/csf/BasicNetwork.h @@ -199,7 +199,7 @@ BasicNetwork::disconnect(Peer const& peer1, Peer const& peer2) { if (!links_.disconnect(peer1, peer2)) return false; - bool r = links_.disconnect(peer2, peer1); + bool const r = links_.disconnect(peer2, peer1); (void)r; assert(r); return true; diff --git a/src/test/csf/BasicNetwork_test.cpp b/src/test/csf/BasicNetwork_test.cpp index eaef805f33..eee16c2ce1 100644 --- a/src/test/csf/BasicNetwork_test.cpp +++ b/src/test/csf/BasicNetwork_test.cpp @@ -33,8 +33,10 @@ public: if (id == 0) { for (auto const link : net.links(this)) + { net.send( this, link.target, [&, to = link.target] { to->receive(net, this, 1); }); + } } else { @@ -51,9 +53,11 @@ public: if (m < 5) { for (auto const link : net.links(this)) + { net.send(this, link.target, [&, mm = m, to = link.target] { to->receive(net, this, mm); }); + } } } }; diff --git a/src/test/csf/Digraph_test.cpp b/src/test/csf/Digraph_test.cpp index a0cb35323d..1c34bbcfec 100644 --- a/src/test/csf/Digraph_test.cpp +++ b/src/test/csf/Digraph_test.cpp @@ -24,13 +24,15 @@ public: BEAST_EXPECT(graph.connect('a', 'b', "foobar")); BEAST_EXPECT(graph.connected('a', 'b')); - BEAST_EXPECT(*graph.edge('a', 'b') == "foobar"); + BEAST_EXPECT( + *graph.edge('a', 'b') == "foobar"); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(!graph.connect('a', 'b', "repeat")); BEAST_EXPECT(graph.disconnect('a', 'b')); BEAST_EXPECT(graph.connect('a', 'b', "repeat")); BEAST_EXPECT(graph.connected('a', 'b')); - BEAST_EXPECT(*graph.edge('a', 'b') == "repeat"); + BEAST_EXPECT( + *graph.edge('a', 'b') == "repeat"); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(graph.connect('a', 'c', "tree")); @@ -55,7 +57,7 @@ public: // only 'a' has out edges BEAST_EXPECT(graph.outVertices().size() == 1); - std::vector expected = {'b', 'c'}; + std::vector const expected = {'b', 'c'}; BEAST_EXPECT((graph.outVertices('a') == expected)); BEAST_EXPECT(graph.outVertices('b').size() == 0); @@ -64,7 +66,7 @@ public: std::stringstream ss; graph.saveDot(ss, [](char v) { return v; }); - std::string expectedDot = + std::string const expectedDot = "digraph {\n" "a -> b;\n" "a -> c;\n" diff --git a/src/test/csf/Histogram.h b/src/test/csf/Histogram.h index 9e7b471a2b..cbc2d42d6c 100644 --- a/src/test/csf/Histogram.h +++ b/src/test/csf/Histogram.h @@ -92,7 +92,7 @@ public: percentile(float p) const { assert(p >= 0 && p <= 1); - std::size_t pos = std::round(p * samples); + std::size_t const pos = std::round(p * samples); if (counts_.empty()) return T{}; diff --git a/src/test/csf/Peer.h b/src/test/csf/Peer.h index c36d600e6c..fb1238990e 100644 --- a/src/test/csf/Peer.h +++ b/src/test/csf/Peer.h @@ -231,7 +231,7 @@ struct Peer // Number of proposers in the prior round std::size_t prevProposers = 0; // Duration of prior round - std::chrono::milliseconds prevRoundTime; + std::chrono::milliseconds prevRoundTime{}; // Quorum of validations needed for a ledger to be fully validated // TODO: Use the logic in ValidatorList to set this dynamically @@ -501,16 +501,10 @@ struct Peer NetClock::duration const& closeResolution, ConsensusCloseTimes const& rawCloseTimes, ConsensusMode const& mode, - Json::Value&& consensusJson) + Json::Value const& consensusJson) { onAccept( - result, - prevLedger, - closeResolution, - rawCloseTimes, - mode, - std::move(consensusJson), - validating()); + result, prevLedger, closeResolution, rawCloseTimes, mode, consensusJson, validating()); } void @@ -520,10 +514,10 @@ struct Peer NetClock::duration const& closeResolution, ConsensusCloseTimes const& rawCloseTimes, ConsensusMode const& mode, - Json::Value&& consensusJson, + Json::Value const& consensusJson, bool const validating) { - schedule(delays.ledgerAccept, [=, this]() { + schedule(delays.ledgerAccept, [mode, result, prevLedger, closeResolution, this]() { bool const proposing = mode == ConsensusMode::proposing; bool const consensusFail = result.state == ConsensusState::MovedOn; @@ -550,9 +544,9 @@ struct Peer if (runAsValidator && isCompatible && !consensusFail && validations.canValidateSeq(newLedger.seq())) { - bool isFull = proposing; + bool const isFull = proposing; - Validation v{newLedger.id(), newLedger.seq(), now(), now(), key, id, isFull}; + Validation const v{newLedger.id(), newLedger.seq(), now(), now(), key, id, isFull}; // share the new validation; it is trusted by the receiver share(v); // we trust ourselves @@ -880,7 +874,7 @@ struct Peer issue(StartRound{bestLCL, lastClosedLedger}); // Not yet modeling dynamic UNL. - hash_set nowUntrusted; + hash_set const nowUntrusted; consensus.startRound(now(), bestLCL, lastClosedLedger, nowUntrusted, runAsValidator, {}); } diff --git a/src/test/csf/PeerGroup.h b/src/test/csf/PeerGroup.h index e900ab9934..5df6de84a6 100644 --- a/src/test/csf/PeerGroup.h +++ b/src/test/csf/PeerGroup.h @@ -313,8 +313,8 @@ randomRankedTrust( Generator& g) { std::vector const groups = randomRankedGroups(peers, ranks, numGroups, sizeDist, g); + std::uniform_int_distribution u(0, groups.size() - 1); // NOLINT(misc-const-correctness) - std::uniform_int_distribution u(0, groups.size() - 1); for (auto& peer : peers) { for (auto& target : groups[u(g)]) @@ -337,8 +337,8 @@ randomRankedConnect( SimDuration delay) { std::vector const groups = randomRankedGroups(peers, ranks, numGroups, sizeDist, g); + std::uniform_int_distribution u(0, groups.size() - 1); // NOLINT(misc-const-correctness) - std::uniform_int_distribution u(0, groups.size() - 1); for (auto& peer : peers) { for (auto& target : groups[u(g)]) diff --git a/src/test/csf/README.md b/src/test/csf/README.md index 30d5abb042..081c9807d7 100644 --- a/src/test/csf/README.md +++ b/src/test/csf/README.md @@ -2,7 +2,7 @@ The Consensus Simulation Framework is a set of software components for describing, running and analyzing simulations of the consensus algorithm in a -controlled manner. It is also used to unit test the generic Ripple consensus +controlled manner. It is also used to unit test the generic XRPL consensus algorithm implementation. The framework is in its early stages, so the design and supported features are subject to change. diff --git a/src/test/csf/Sim.h b/src/test/csf/Sim.h index 3525258c59..e5f64cae1f 100644 --- a/src/test/csf/Sim.h +++ b/src/test/csf/Sim.h @@ -129,8 +129,8 @@ public: Nodes in the group are synchronized if they share the same last fully validated and last generated ledger. */ - bool - synchronized(PeerGroup const& g) const; + static bool + synchronized(PeerGroup const& g); /** Check whether all peers in the network are synchronized */ diff --git a/src/test/csf/TrustGraph.h b/src/test/csf/TrustGraph.h index bae0be4af7..3f1fcae0c1 100644 --- a/src/test/csf/TrustGraph.h +++ b/src/test/csf/TrustGraph.h @@ -122,9 +122,9 @@ public: { auto const& unlA = uniqueUNLs[i]; auto const& unlB = uniqueUNLs[j]; - double rhs = 2.0 * (1. - quorum) * std::max(unlA.size(), unlB.size()); + double const rhs = 2.0 * (1. - quorum) * std::max(unlA.size(), unlB.size()); - int intersectionSize = std::count_if( + int const intersectionSize = std::count_if( unlA.begin(), unlA.end(), [&](Peer p) { return unlB.find(p) != unlB.end(); }); if (intersectionSize < rhs) diff --git a/src/test/csf/Tx.h b/src/test/csf/Tx.h index f919b650db..81393f7bda 100644 --- a/src/test/csf/Tx.h +++ b/src/test/csf/Tx.h @@ -100,7 +100,8 @@ public: { } - TxSet(MutableTxSet&& m) : txs_{std::move(m.txs_)}, id_{calcID(txs_)} + TxSet(MutableTxSet&& m) // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) + : txs_{m.txs_}, id_{calcID(txs_)} { } @@ -161,7 +162,7 @@ private: TxSetType txs_; //! The unique ID of this tx set - ID id_; + ID id_{}; }; //------------------------------------------------------------------------------ diff --git a/src/test/csf/Validation.h b/src/test/csf/Validation.h index 5ebbcf3ae5..ba4da148f4 100644 --- a/src/test/csf/Validation.h +++ b/src/test/csf/Validation.h @@ -129,7 +129,7 @@ public: Validation const& unwrap() const { - // For the rippled implementation in which RCLValidation wraps + // For the xrpld implementation in which RCLValidation wraps // STValidation, the csf::Validation has no more specific type it // wraps, so csf::Validation unwraps to itself return *this; diff --git a/src/test/csf/impl/Sim.cpp b/src/test/csf/impl/Sim.cpp index 4fbee56f0f..a775dd30ff 100644 --- a/src/test/csf/impl/Sim.cpp +++ b/src/test/csf/impl/Sim.cpp @@ -33,7 +33,7 @@ Sim::synchronized() const } bool -Sim::synchronized(PeerGroup const& g) const +Sim::synchronized(PeerGroup const& g) { if (g.size() < 1) return true; diff --git a/src/test/csf/impl/ledgers.cpp b/src/test/csf/impl/ledgers.cpp index 12d461a214..9b0a4e3973 100644 --- a/src/test/csf/impl/ledgers.cpp +++ b/src/test/csf/impl/ledgers.cpp @@ -42,14 +42,14 @@ mismatch(Ledger const& a, Ledger const& b) // end is 1 past end of range Seq start{0}; - Seq end = std::min(a.seq() + Seq{1}, b.seq() + Seq{1}); + Seq const end = std::min(a.seq() + Seq{1}, b.seq() + Seq{1}); // Find mismatch in [start,end) // Binary search Seq count = end - start; while (count > Seq{0}) { - Seq step = count / Seq{2}; + Seq const step = count / Seq{2}; Seq curr = start + step; if (a[curr] == b[curr]) { @@ -58,7 +58,9 @@ mismatch(Ledger const& a, Ledger const& b) count -= step + Seq{1}; } else + { count = step; + } } return start; } @@ -88,9 +90,13 @@ LedgerOracle::accept( next.closeTimeResolution = closeTimeResolution; next.closeTimeAgree = consensusCloseTime != NetClock::time_point{}; if (next.closeTimeAgree) + { next.closeTime = effCloseTime(consensusCloseTime, closeTimeResolution, parent.closeTime()); + } else + { next.closeTime = parent.closeTime() + 1s; + } next.parentCloseTime = parent.closeTime(); next.parentID = parent.id(); @@ -117,7 +123,7 @@ LedgerOracle::lookup(Ledger::ID const& id) const } std::size_t -LedgerOracle::branches(std::set const& ledgers) const +LedgerOracle::branches(std::set const& ledgers) { // Tips always maintains the Ledgers with largest sequence number // along all known chains. diff --git a/src/test/csf/ledgers.h b/src/test/csf/ledgers.h index 9da1c2182a..67a7427af6 100644 --- a/src/test/csf/ledgers.h +++ b/src/test/csf/ledgers.h @@ -2,13 +2,12 @@ #include -#include - #include #include #include #include #include +#include #include @@ -278,8 +277,8 @@ public: O \--> B */ - std::size_t - branches(std::set const& ledgers) const; + static std::size_t + branches(std::set const& ledgers); }; /** Helper for writing unit tests with controlled ledger histories. diff --git a/src/test/csf/random.h b/src/test/csf/random.h index fc5098af32..f3ecca1dbc 100644 --- a/src/test/csf/random.h +++ b/src/test/csf/random.h @@ -25,7 +25,7 @@ random_weighted_shuffle(std::vector v, std::vector w, G& g) for (int i = 0; i < v.size() - 1; ++i) { // pick a random item weighted by w - std::discrete_distribution<> dd(w.begin() + i, w.end()); + std::discrete_distribution<> dd(w.begin() + i, w.end()); // NOLINT(misc-const-correctness) auto idx = dd(g); std::swap(v[i], v[idx]); std::swap(w[i], w[idx]); diff --git a/src/test/csf/timers.h b/src/test/csf/timers.h index c8d71d5b7a..beb4e142d9 100644 --- a/src/test/csf/timers.h +++ b/src/test/csf/timers.h @@ -47,11 +47,11 @@ public: beat(SimTime when) { using namespace std::chrono; - RealTime realTime = RealClock::now(); - SimTime simTime = when; + RealTime const realTime = RealClock::now(); + SimTime const simTime = when; - RealDuration realDuration = realTime - startRealTime_; - SimDuration simDuration = simTime - startSimTime_; + RealDuration const realDuration = realTime - startRealTime_; + SimDuration const simDuration = simTime - startSimTime_; out_ << "Heartbeat. Time Elapsed: {sim: " << duration_cast(simDuration).count() << "s | real: " << duration_cast(realDuration).count() << "s}\n" << std::flush; diff --git a/src/test/jtx.h b/src/test/jtx.h index 903b029f29..d4b88b0b9e 100644 --- a/src/test/jtx.h +++ b/src/test/jtx.h @@ -51,7 +51,6 @@ #include #include #include -#include #include #include #include diff --git a/src/test/jtx/AMMTest.h b/src/test/jtx/AMMTest.h index be6e228c0a..a311d9c638 100644 --- a/src/test/jtx/AMMTest.h +++ b/src/test/jtx/AMMTest.h @@ -83,24 +83,24 @@ protected: */ void testAMM( - std::function&& cb, + std::function const& cb, std::optional> const& pool = std::nullopt, std::uint16_t tfee = 0, std::optional const& ter = std::nullopt, std::vector const& features = {testable_amendments()}); void - testAMM(std::function&& cb, TestAMMArg const& arg); + testAMM(std::function const& cb, TestAMMArg const& arg); }; class AMMTest : public jtx::AMMTestBase { protected: - XRPAmount - reserve(jtx::Env& env, std::uint32_t count) const; + static XRPAmount + reserve(jtx::Env& env, std::uint32_t count); - XRPAmount - ammCrtFee(jtx::Env& env) const; + static XRPAmount + ammCrtFee(jtx::Env& env); /* Path_test */ /************************************************/ @@ -127,7 +127,7 @@ protected: void signal() { - std::lock_guard lk(mutex_); + std::lock_guard const lk(mutex_); signaled_ = true; cv_.notify_all(); } diff --git a/src/test/jtx/AbstractClient.h b/src/test/jtx/AbstractClient.h index 9b1ef178ca..94e58f60cd 100644 --- a/src/test/jtx/AbstractClient.h +++ b/src/test/jtx/AbstractClient.h @@ -5,10 +5,10 @@ namespace xrpl { namespace test { -/* Abstract Ripple Client interface. +/* Abstract XRPL client interface. This abstracts the transport layer, allowing - commands to be submitted to a rippled server. + commands to be submitted to an xrpld server. */ class AbstractClient { diff --git a/src/test/jtx/CaptureLogs.h b/src/test/jtx/CaptureLogs.h index 8419c4dc70..8c8da6817b 100644 --- a/src/test/jtx/CaptureLogs.h +++ b/src/test/jtx/CaptureLogs.h @@ -37,14 +37,14 @@ class CaptureLogs : public Logs void write(beast::severities::Severity level, std::string const& text) override { - std::lock_guard lock(strmMutex_); + std::lock_guard const lock(strmMutex_); strm_ << text; } void writeAlways(beast::severities::Severity level, std::string const& text) override { - std::lock_guard lock(strmMutex_); + std::lock_guard const lock(strmMutex_); strm_ << text; } }; diff --git a/src/test/jtx/Env.h b/src/test/jtx/Env.h index 2ac0ca7435..b494ade31c 100644 --- a/src/test/jtx/Env.h +++ b/src/test/jtx/Env.h @@ -11,17 +11,17 @@ #include #include -#include #include #include -#include #include +#include #include #include #include #include #include +#include #include #include #include @@ -189,7 +189,7 @@ public: beast::severities::Severity thresh = beast::severities::kError) : test(suite_) , bundle_(suite_, std::move(config), std::move(logs), thresh) - , journal{bundle_.app->journal("Env")} + , journal{bundle_.app->getJournal("Env")} { memoize(Account::master); Pathfinder::initPathTable(); @@ -328,7 +328,7 @@ public: std::shared_ptr current() const { - return app().openLedger().current(); + return app().getOpenLedger().current(); } /** Returns the last closed ledger. @@ -510,6 +510,7 @@ public: */ // VFALCO NOTE This should return a unit-less amount PrettyAmount + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) balance(Account const& account, Asset const& asset) const; PrettyAmount diff --git a/src/test/jtx/Env_ss.h b/src/test/jtx/Env_ss.h index 9db96d5907..d6f5fd1a29 100644 --- a/src/test/jtx/Env_ss.h +++ b/src/test/jtx/Env_ss.h @@ -24,7 +24,7 @@ private: operator=(SignSubmitRunner&&) = delete; SignSubmitRunner(Env& env, JTx&& jt, std::source_location loc) - : env_(env), jt_(jt), loc_(loc) + : env_(env), jt_(std::move(jt)), loc_(loc) { } diff --git a/src/test/jtx/Env_test.cpp b/src/test/jtx/Env_test.cpp index 6d151c27ce..4e9f0d99e2 100644 --- a/src/test/jtx/Env_test.cpp +++ b/src/test/jtx/Env_test.cpp @@ -28,7 +28,7 @@ public: } // Declarations in Account.h - void + static void testAccount() { using namespace jtx; @@ -37,11 +37,11 @@ public: Account b(a); a = b; a = std::move(b); - Account c(std::move(a)); + Account const c(std::move(a)); } - Account("alice"); - Account("alice", KeyType::secp256k1); - Account("alice", KeyType::ed25519); + Account("alice"); // NOLINT(bugprone-unused-raii) + Account("alice", KeyType::secp256k1); // NOLINT(bugprone-unused-raii) + Account("alice", KeyType::ed25519); // NOLINT(bugprone-unused-raii) auto const gw = Account("gw"); [](AccountID) {}(gw); auto const USD = gw["USD"]; @@ -56,11 +56,11 @@ public: { using namespace jtx; - PrettyAmount(0); - PrettyAmount(1); - PrettyAmount(0u); - PrettyAmount(1u); - PrettyAmount(-1); + PrettyAmount(0); // NOLINT(bugprone-unused-raii) + PrettyAmount(1); // NOLINT(bugprone-unused-raii) + PrettyAmount(0u); // NOLINT(bugprone-unused-raii) + PrettyAmount(1u); // NOLINT(bugprone-unused-raii) + PrettyAmount(-1); // NOLINT(bugprone-unused-raii) static_assert(!std::is_trivially_constructible::value, ""); static_assert(!std::is_trivially_constructible::value, ""); static_assert(!std::is_trivially_constructible::value, ""); @@ -533,14 +533,14 @@ public: BEAST_EXPECT(*jt1.get() == 7); BEAST_EXPECT(!jt1.get()); JTx jt2(std::move(jt1)); - BEAST_EXPECT(!jt1.get()); - BEAST_EXPECT(!jt1.get()); + BEAST_EXPECT(!jt1.get()); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(!jt1.get()); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(jt2.get()); BEAST_EXPECT(*jt2.get() == 7); BEAST_EXPECT(!jt2.get()); jt1 = std::move(jt2); - BEAST_EXPECT(!jt2.get()); - BEAST_EXPECT(!jt2.get()); + BEAST_EXPECT(!jt2.get()); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(!jt2.get()); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(jt1.get()); BEAST_EXPECT(*jt1.get() == 7); BEAST_EXPECT(!jt1.get()); @@ -635,9 +635,10 @@ public: std::uint32_t const aliceSeq = env.seq("alice"); // Sign jsonNoop. - Json::Value jsonNoop = env.json(noop("alice"), fee(baseFee), seq(aliceSeq), sig("alice")); + Json::Value const jsonNoop = + env.json(noop("alice"), fee(baseFee), seq(aliceSeq), sig("alice")); // Re-sign jsonNoop. - JTx jt = env.jt(jsonNoop); + JTx const jt = env.jt(jsonNoop); env(jt); } @@ -707,8 +708,10 @@ public: auto const neverSupportedFeat = [&]() -> std::optional { auto const n = supported.size(); for (size_t i = 0; i < n; ++i) + { if (!supported[i]) return bitsetIndexToFeature(i); + } return std::nullopt; }(); @@ -721,7 +724,7 @@ public: } auto hasFeature = [](Env& env, uint256 const& f) { - return (env.app().config().features.find(f) != env.app().config().features.end()); + return (env.app().config().features.contains(f)); }; { @@ -750,7 +753,7 @@ public: Env env{*this, missingSomeFeatures}; BEAST_EXPECT(env.app().config().features.size() == (supported.count() - 2)); foreachFeature(supported, [&](uint256 const& f) { - bool hasnot = (f == featureDynamicMPT || f == featureTokenEscrow); + bool const hasnot = (f == featureDynamicMPT || f == featureTokenEscrow); this->BEAST_EXPECT(hasnot != hasFeature(env, f)); }); } @@ -769,7 +772,7 @@ public: BEAST_EXPECT(hasFeature(env, *neverSupportedFeat)); foreachFeature(supported, [&](uint256 const& f) { - bool has = (f == featureDynamicMPT || f == featureTokenEscrow); + bool const has = (f == featureDynamicMPT || f == featureTokenEscrow); this->BEAST_EXPECT(has == hasFeature(env, f)); }); } @@ -785,7 +788,7 @@ public: BEAST_EXPECT(env.app().config().features.size() == (supported.count() - 2 + 1)); BEAST_EXPECT(hasFeature(env, *neverSupportedFeat)); foreachFeature(supported, [&](uint256 const& f) { - bool hasnot = (f == featureDynamicMPT || f == featureTokenEscrow); + bool const hasnot = (f == featureDynamicMPT || f == featureTokenEscrow); this->BEAST_EXPECT(hasnot != hasFeature(env, f)); }); } @@ -809,7 +812,7 @@ public: testExceptionalShutdown() { except([this] { - jtx::Env env{ + jtx::Env const env{ *this, jtx::envconfig([](std::unique_ptr cfg) { (*cfg).deprecatedClearSection("port_rpc"); diff --git a/src/test/jtx/JTx.h b/src/test/jtx/JTx.h index 90fbb8a6b6..bf43d0aa75 100644 --- a/src/test/jtx/JTx.h +++ b/src/test/jtx/JTx.h @@ -38,9 +38,6 @@ struct JTx // Functions that sign something else after the mainSigners, such as // sfCounterpartySignature std::vector> postSigners; - // Metadata about the unit test itself - // The line where the JTx was constructed - std::optional testLine = std::nullopt; JTx() = default; JTx(JTx const&) = default; diff --git a/src/test/jtx/Oracle.h b/src/test/jtx/Oracle.h index 8efd17802c..8f48ad4d37 100644 --- a/src/test/jtx/Oracle.h +++ b/src/test/jtx/Oracle.h @@ -81,7 +81,7 @@ struct RemoveArg std::optional const& err = std::nullopt; }; -// Simulate testStartTime as 10'000s from Ripple epoch time to make +// Simulate testStartTime as 10'000s from XRPL epoch time to make // LastUpdateTime validation to work and to make unit-test consistent. // The value doesn't matter much, it has to be greater // than maxLastUpdateTimeDelta in order to pass LastUpdateTime @@ -99,7 +99,7 @@ private: static inline std::uint32_t fee = 0; Env& env_; AccountID owner_; - std::uint32_t documentID_; + std::uint32_t documentID_{}; private: void diff --git a/src/test/jtx/PathSet.h b/src/test/jtx/PathSet.h index a363f9dff9..c522ed635e 100644 --- a/src/test/jtx/PathSet.h +++ b/src/test/jtx/PathSet.h @@ -3,6 +3,7 @@ #include #include +#include #include namespace xrpl { diff --git a/src/test/jtx/TestHelpers.h b/src/test/jtx/TestHelpers.h index 2836748ec3..4da086b05b 100644 --- a/src/test/jtx/TestHelpers.h +++ b/src/test/jtx/TestHelpers.h @@ -317,7 +317,7 @@ template Json::Value getAccountLines(Env& env, AccountID const& acctId, IOU... ious) { - auto const jrr = getAccountLines(env, acctId); + auto jrr = getAccountLines(env, acctId); Json::Value res; for (auto const& line : jrr[jss::lines]) { @@ -559,7 +559,6 @@ allPathElements(AccountID const& a, Issue const& iss); namespace check { /** Create a check. */ -// clang-format off template requires std::is_same_v Json::Value @@ -572,7 +571,6 @@ create(A const& account, A const& dest, STAmount const& sendMax) jv[sfTransactionType.jsonName] = jss::CheckCreate; return jv; } -// clang-format on inline Json::Value create(jtx::Account const& account, jtx::Account const& dest, STAmount const& sendMax) @@ -598,7 +596,7 @@ checkMetrics( std::uint64_t expectedMedFeeLevel = minEscalationFeeLevel.fee(), std::source_location const location = std::source_location::current()) { - int line = location.line(); + int const line = location.line(); char const* file = location.file_name(); FeeLevel64 const expectedMin{expectedMinFeeLevel}; FeeLevel64 const expectedMed{expectedMedFeeLevel}; diff --git a/src/test/jtx/TrustedPublisherServer.h b/src/test/jtx/TrustedPublisherServer.h index 529383c622..c9304426cf 100644 --- a/src/test/jtx/TrustedPublisherServer.h +++ b/src/test/jtx/TrustedPublisherServer.h @@ -129,7 +129,7 @@ public: // TrustedPublisherServer must be accessed through a shared_ptr. // This constructor is only public so std::make_shared has access. - // The function`make_TrustedPublisherServer` should be used to create + // The function `make_TrustedPublisherServer` should be used to create // instances. // The `futures` member is expected to be structured as // effective / expiration time point pairs for use in version 2 UNLs @@ -170,7 +170,7 @@ public: } data.pop_back(); data += "]}"; - std::string blob = base64_encode(data); + std::string const blob = base64_encode(data); return std::make_pair(data, blob); }(); auto const sig = strHex(sign(keys.first, keys.second, makeSlice(data))); @@ -198,7 +198,7 @@ public: } data.pop_back(); data += "]}"; - std::string blob = base64_encode(data); + std::string const blob = base64_encode(data); auto const sig = strHex(sign(keys.first, keys.second, makeSlice(data))); blobInfo.emplace_back(blob, sig); } @@ -249,7 +249,7 @@ public: { error_code ec; acceptor_.close(ec); - // TODO consider making this join + // TODO: consider making this join // any running do_peer threads } @@ -295,7 +295,7 @@ public: openssl genrsa -out ca.key 2048 openssl req -new -x509 -nodes -days 10000 -key ca.key -out ca.crt \ -subj "/C=US/ST=CA/L=Los - Angeles/O=rippled-unit-tests/CN=example.com" # generate private cert + Angeles/O=xrpld-unit-tests/CN=example.com" # generate private cert openssl genrsa -out server.key 2048 # Generate certificate signing request # since our unit tests can run in either ipv4 or ipv6 mode, @@ -318,7 +318,7 @@ public: openssl req -new -key server.key -out server.csr \ -config extras.cnf \ -subj "/C=US/ST=California/L=San - Francisco/O=rippled-unit-tests/CN=127.0.0.1" \ + Francisco/O=xrpld-unit-tests/CN=127.0.0.1" \ # Create public certificate by signing with our CA openssl x509 -req -days 10000 -in server.csr -CA ca.crt -CAkey ca.key @@ -562,7 +562,7 @@ private: res.result(http::status::ok); res.insert("Content-Type", "text/example"); // if huge was requested, lie about content length - std::uint64_t cl = boost::starts_with(path, "/textfile/huge") + std::uint64_t const cl = boost::starts_with(path, "/textfile/huge") ? std::numeric_limits::max() : 1024; res.content_length(cl); diff --git a/src/test/jtx/amount.h b/src/test/jtx/amount.h index c4e97502db..1912f01330 100644 --- a/src/test/jtx/amount.h +++ b/src/test/jtx/amount.h @@ -196,8 +196,8 @@ public: PrettyAmount operator()(Number v, Number::rounding_mode rounding = Number::getround()) const { - NumberRoundModeGuard mg(rounding); - STAmount amount{asset_, v * scale_}; + NumberRoundModeGuard const mg(rounding); + STAmount const amount{asset_, v * scale_}; return {amount, ""}; } diff --git a/src/test/jtx/did.h b/src/test/jtx/did.h index 9cd98a1324..e6b06f8f7c 100644 --- a/src/test/jtx/did.h +++ b/src/test/jtx/did.h @@ -53,7 +53,7 @@ public: } }; -/** Sets the optional Attestation on a DIDSet. */ +/** Sets the optional Data on a DIDSet. */ class data { private: diff --git a/src/test/jtx/flags.h b/src/test/jtx/flags.h index 3dde1fa414..a203f1461e 100644 --- a/src/test/jtx/flags.h +++ b/src/test/jtx/flags.h @@ -12,7 +12,7 @@ namespace detail { class flags_helper { protected: - std::uint32_t mask_; + std::uint32_t mask_{0}; private: void @@ -79,7 +79,7 @@ private: protected: template - flags_helper(Args... args) : mask_(0) + flags_helper(Args... args) { set_args(args...); } diff --git a/src/test/jtx/impl/AMM.cpp b/src/test/jtx/impl/AMM.cpp index bb159d6bc1..b06353a30d 100644 --- a/src/test/jtx/impl/AMM.cpp +++ b/src/test/jtx/impl/AMM.cpp @@ -1,12 +1,13 @@ #include #include +#include +#include +#include #include #include #include #include -#include -#include namespace xrpl { namespace test { @@ -52,8 +53,6 @@ AMM::AMM( , log_(log) , doClose_(close) , lastPurchasePrice_(0) - , bidMin_() - , bidMax_() , msig_(ms) , fee_(fee) , ammAccount_(create(tfee, flags, seq, ter)) @@ -122,9 +121,13 @@ AMM::create( if (flags) jv[jss::Flags] = *flags; if (fee_ != 0) + { jv[sfFee] = std::to_string(fee_); + } else + { jv[jss::Fee] = std::to_string(env_.current()->fees().increment.drops()); + } submit(jv, seq, ter); if (!ter || env_.ter() == tesSUCCESS) @@ -217,6 +220,7 @@ IOUAmount AMM::getLPTokensBalance(std::optional const& account) const { if (account) + { return accountHolds( *env_.current(), *account, @@ -224,6 +228,7 @@ AMM::getLPTokensBalance(std::optional const& account) const FreezeHandling::fhZERO_IF_FROZEN, env_.journal) .iou(); + } if (auto const amm = env_.current()->read(keylet::amm(asset1_.issue(), asset2_.issue()))) return amm->getFieldAmount(sfLPTokenBalance).iou(); return IOUAmount{0}; @@ -438,18 +443,28 @@ AMM::deposit( // If including asset1In and asset2In or tokens as // deposit min amounts then must set the flags // explicitly instead of relying on this logic. - if (!(jvFlags & tfDepositSubTx)) + if ((jvFlags & tfDepositSubTx) == 0u) { if (tokens && !asset1In) + { jvFlags |= tfLPToken; + } else if (tokens && asset1In) + { jvFlags |= tfOneAssetLPToken; + } else if (asset1In && asset2In) + { jvFlags |= tfTwoAsset; + } else if (maxEP && asset1In) + { jvFlags |= tfLimitLPToken; + } else if (asset1In) + { jvFlags |= tfSingleAsset; + } } jv[jss::Flags] = jvFlags; return deposit(account, jv, assets, seq, ter); @@ -558,18 +573,28 @@ AMM::withdraw( std::uint32_t jvFlags = 0; if (flags) jvFlags = *flags; - if (!(jvFlags & tfWithdrawSubTx)) + if ((jvFlags & tfWithdrawSubTx) == 0u) { if (tokens && !asset1Out) + { jvFlags |= tfLPToken; + } else if (asset1Out && asset2Out) + { jvFlags |= tfTwoAsset; + } else if (tokens && asset1Out) + { jvFlags |= tfOneAssetLPToken; + } else if (asset1Out && maxEP) + { jvFlags |= tfLimitLPToken; + } else if (asset1Out) + { jvFlags |= tfSingleAsset; + } } jv[jss::Flags] = jvFlags; return withdraw(account, jv, seq, assets, ter); @@ -614,7 +639,7 @@ AMM::vote( void AMM::vote(VoteArg const& arg) { - return vote(arg.account, arg.tfee, arg.flags, arg.seq, arg.assets, arg.err); + vote(arg.account, arg.tfee, arg.flags, arg.seq, arg.assets, arg.err); } Json::Value @@ -627,7 +652,8 @@ AMM::bid(BidArg const& arg) amm->isFieldPresent(sfAuctionSlot)); if (amm->isFieldPresent(sfAuctionSlot)) { - auto const& auctionSlot = static_cast(amm->peekAtField(sfAuctionSlot)); + auto const& auctionSlot = + safe_downcast(amm->peekAtField(sfAuctionSlot)); lastPurchasePrice_ = auctionSlot[sfPrice].iou(); } } @@ -639,25 +665,29 @@ AMM::bid(BidArg const& arg) setTokens(jv, arg.assets); auto getBid = [&](auto const& bid) { if (std::holds_alternative(bid)) + { return STAmount{lptIssue_, std::get(bid)}; - else if (std::holds_alternative(bid)) + } + if (std::holds_alternative(bid)) + { return toSTAmount(std::get(bid), lptIssue_); - else - return std::get(bid); + } + + return std::get(bid); }; if (arg.bidMin) { - STAmount saTokens = getBid(*arg.bidMin); + STAmount const saTokens = getBid(*arg.bidMin); saTokens.setJson(jv[jss::BidMin]); bidMin_ = saTokens.iou(); } if (arg.bidMax) { - STAmount saTokens = getBid(*arg.bidMax); + STAmount const saTokens = getBid(*arg.bidMax); saTokens.setJson(jv[jss::BidMax]); bidMax_ = saTokens.iou(); } - if (arg.authAccounts.size() > 0) + if (!arg.authAccounts.empty()) { Json::Value accounts(Json::arrayValue); for (auto const& account : arg.authAccounts) @@ -689,22 +719,38 @@ AMM::submit( if (msig_) { if (seq && ter) + { env_(jv, *msig_, *seq, *ter); + } else if (seq) + { env_(jv, *msig_, *seq); + } else if (ter) + { env_(jv, *msig_, *ter); + } else + { env_(jv, *msig_); + } } else if (seq && ter) + { env_(jv, *seq, *ter); + } else if (seq) + { env_(jv, *seq); + } else if (ter) + { env_(jv, *ter); + } else + { env_(jv); + } if (doClose_) env_.close(); } @@ -719,7 +765,8 @@ AMM::expectAuctionSlot(auto&& cb) const amm->isFieldPresent(sfAuctionSlot)); if (amm->isFieldPresent(sfAuctionSlot)) { - auto const& auctionSlot = static_cast(amm->peekAtField(sfAuctionSlot)); + auto const& auctionSlot = + safe_downcast(amm->peekAtField(sfAuctionSlot)); if (auctionSlot.isFieldPresent(sfAccount)) { // This could fail in pre-fixInnerObjTemplate tests @@ -728,7 +775,7 @@ AMM::expectAuctionSlot(auto&& cb) const // to avoid the failure. auto const slotFee = auctionSlot[~sfDiscountedFee].value_or(0); auto const slotInterval = ammAuctionTimeSlot( - env_.app().timeKeeper().now().time_since_epoch().count(), auctionSlot); + env_.app().getTimeKeeper().now().time_since_epoch().count(), auctionSlot); auto const slotPrice = auctionSlot[sfPrice].iou(); auto const authAccounts = auctionSlot.getFieldArray(sfAuthAccounts); return cb(slotFee, slotInterval, slotPrice, authAccounts); diff --git a/src/test/jtx/impl/AMMTest.cpp b/src/test/jtx/impl/AMMTest.cpp index 01ba5069bf..9d9a537210 100644 --- a/src/test/jtx/impl/AMMTest.cpp +++ b/src/test/jtx/impl/AMMTest.cpp @@ -82,18 +82,17 @@ AMMTestBase::AMMTestBase() void AMMTestBase::testAMM( - std::function&& cb, + std::function const& cb, std::optional> const& pool, std::uint16_t tfee, std::optional const& ter, std::vector const& vfeatures) { - testAMM( - std::move(cb), TestAMMArg{.pool = pool, .tfee = tfee, .ter = ter, .features = vfeatures}); + testAMM(cb, TestAMMArg{.pool = pool, .tfee = tfee, .ter = ter, .features = vfeatures}); } void -AMMTestBase::testAMM(std::function&& cb, TestAMMArg const& arg) +AMMTestBase::testAMM(std::function const& cb, TestAMMArg const& arg) { using namespace jtx; @@ -120,7 +119,7 @@ AMMTestBase::testAMM(std::function&& cb, TestAMMArg return defXRP; return a + XRP(1000); } - auto const defIOU = STAmount{a.issue(), 30000}; + auto defIOU = STAmount{a.issue(), 30000}; if (a <= defIOU) return defIOU; return a + STAmount{a.issue(), 1000}; @@ -130,11 +129,17 @@ AMMTestBase::testAMM(std::function&& cb, TestAMMArg BEAST_EXPECT(asset1 <= toFund1 && asset2 <= toFund2); if (!asset1.native() && !asset2.native()) + { fund(env, gw, {alice, carol}, {toFund1, toFund2}, Fund::All); + } else if (asset1.native()) + { fund(env, gw, {alice, carol}, toFund1, {toFund2}, Fund::All); + } else if (asset2.native()) + { fund(env, gw, {alice, carol}, toFund2, {toFund1}, Fund::All); + } AMM ammAlice( env, alice, asset1, asset2, CreateArg{.log = false, .tfee = arg.tfee, .err = arg.ter}); @@ -144,13 +149,13 @@ AMMTestBase::testAMM(std::function&& cb, TestAMMArg } XRPAmount -AMMTest::reserve(jtx::Env& env, std::uint32_t count) const +AMMTest::reserve(jtx::Env& env, std::uint32_t count) { return env.current()->fees().accountReserve(count); } XRPAmount -AMMTest::ammCrtFee(jtx::Env& env) const +AMMTest::ammCrtFee(jtx::Env& env) { return env.current()->fees().increment; } @@ -264,6 +269,7 @@ AMMTest::find_paths( Json::Value p; p["Paths"] = path[jss::paths_computed]; STParsedJSONObject po("generic", p); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) paths = po.object->getFieldPathSet(sfPaths); } } diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index 4dfd2f2b38..a6344b5ab1 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -61,7 +61,7 @@ Env::AppBundle::AppBundle( config->SSL_VERIFY_DIR, config->SSL_VERIFY_FILE, config->SSL_VERIFY, debugLog()); owned = make_Application(std::move(config), std::move(logs), std::move(timeKeeper_)); app = owned.get(); - app->logs().threshold(thresh); + app->getLogs().threshold(thresh); if (!app->setup({})) Throw("Env::AppBundle: setup failed"); timeKeeper->set(app->getLedgerMaster().getClosedLedger()->header().closeTime); @@ -76,7 +76,7 @@ Env::AppBundle::~AppBundle() client.reset(); // Make sure all jobs finish, otherwise tests // might not get the coverage they expect. - if (app) + if (app != nullptr) { app->getJobQueue().rendezvous(); app->signalStop("~AppBundle"); @@ -107,7 +107,9 @@ Env::close(NetClock::time_point closeTime, std::optionalgetFieldU64(sfOutstandingAmount), 0, true}; return {amount, lookup(issuer).name()}; } - else - { - // Holder balance - auto const sle = le(keylet::mptoken(id, account)); - if (!sle) - return {STAmount(mptIssue, 0), account.name()}; - STAmount const amount{mptIssue, sle->getFieldU64(sfMPTAmount)}; - return {amount, lookup(issuer).name()}; - } + // Holder balance + auto const sle = le(keylet::mptoken(id, account)); + if (!sle) + return {STAmount(mptIssue, 0), account.name()}; + + STAmount const amount{mptIssue, sle->getFieldU64(sfMPTAmount)}; + return {amount, lookup(issuer).name()}; } PrettyAmount +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) Env::balance(Account const& account, Asset const& asset) const { return std::visit([&](auto const& issue) { return balance(account, issue); }, asset.value()); @@ -336,10 +343,14 @@ Env::parseResult(Json::Value const& jr) parsed.rpcCode.emplace(rpcSUCCESS); } else + { error(parsed, result); + } } else + { error(parsed, jr); + } return parsed; } @@ -362,16 +373,14 @@ Env::submit(JTx const& jt, std::source_location const& loc) return jr; } - else - { - // Parsing failed or the JTx is - // otherwise missing the stx field. - parsedResult.ter = ter_ = temMALFORMED; - return Json::Value(); - } + // Parsing failed or the JTx is + // otherwise missing the stx field. + parsedResult.ter = ter_ = temMALFORMED; + + return Json::Value(); }(); - return postconditions(jt, parsedResult, jr, loc); + postconditions(jt, parsedResult, jr, loc); } void @@ -409,7 +418,7 @@ Env::sign_and_submit(JTx const& jt, Json::Value params, std::source_location con test.expect(parsedResult.ter, "ter uninitialized!"); ter_ = parsedResult.ter.value_or(telENV_RPC_FAILED); - return postconditions(jt, parsedResult, jr, loc); + postconditions(jt, parsedResult, jr, loc); } void @@ -419,16 +428,15 @@ Env::postconditions( Json::Value const& jr, std::source_location const& loc) { - auto const line = jt.testLine ? " (" + to_string(*jt.testLine) + ")" : ""; auto const locStr = std::string("(") + loc.file_name() + ":" + to_string(loc.line()) + ")"; - bool bad = !test.expect(parsed.ter, "apply " + locStr + ": No ter result!" + line); + bool bad = !test.expect(parsed.ter, "apply " + locStr + ": No ter result!"); bad = (jt.ter && parsed.ter && !test.expect( *parsed.ter == *jt.ter, "apply " + locStr + ": Got " + transToken(*parsed.ter) + " (" + transHuman(*parsed.ter) + "); Expected " + transToken(*jt.ter) + " (" + - transHuman(*jt.ter) + ")" + line)); + transHuman(*jt.ter) + ")")); using namespace std::string_literals; bad = (jt.rpcCode && !test.expect( @@ -438,7 +446,7 @@ Env::postconditions( : "NO RESULT") + " (" + parsed.rpcMessage + "); Expected " + RPC::get_error_info(jt.rpcCode->first).token.c_str() + " (" + - jt.rpcCode->second + ")" + line)) || + jt.rpcCode->second + ")")) || bad; // If we have an rpcCode (just checked), then the rpcException check is // optional - the 'error' field may not be defined, but if it is, it must @@ -450,7 +458,7 @@ Env::postconditions( (!jt.rpcException->second || parsed.rpcException == *jt.rpcException->second)), "apply " + locStr + ": Got RPC result "s + parsed.rpcError + " (" + parsed.rpcException + "); Expected " + jt.rpcException->first + " (" + - jt.rpcException->second.value_or("n/a") + ")" + line)) || + jt.rpcException->second.value_or("n/a") + ")")) || bad; if (bad) { @@ -461,7 +469,7 @@ Env::postconditions( // we didn't get the expected result. return; } - if (trace_) + if (trace_ != 0) { if (trace_ > 0) --trace_; @@ -504,7 +512,7 @@ Env::autofill_sig(JTx& jt) { auto& jv = jt.jv; - scope_success success([&]() { + scope_success const success([&]() { // Call all the post-signers after the main signers or autofill are done for (auto const& signer : jt.postSigners) signer(*this, jt); @@ -533,9 +541,13 @@ Env::autofill_sig(JTx& jt) } auto const ar = le(account); if (ar && ar->isFieldPresent(sfRegularKey)) + { jtx::sign(jv, lookup(ar->getAccountID(sfRegularKey))); + } else + { jtx::sign(jv, account); + } } void @@ -549,7 +561,7 @@ Env::autofill(JTx& jt) if (jt.fill_netid) { - uint32_t networkID = app().getNetworkIDService().getNetworkID(); + uint32_t const networkID = app().getNetworkIDService().getNetworkID(); if (!jv.isMember(jss::NetworkID) && networkID > 1024) jv[jss::NetworkID] = std::to_string(networkID); } @@ -625,14 +637,14 @@ Env::do_rpc( std::vector const& args, std::unordered_map const& headers) { - auto response = rpcClient(args, app().config(), app().logs(), apiVersion, headers); + auto response = rpcClient(args, app().config(), app().getLogs(), apiVersion, headers); for (unsigned ctr = 0; (ctr < retries_) and (response.first == rpcINTERNAL); ++ctr) { JLOG(journal.error()) << "Env::do_rpc error, retrying, attempt #" << ctr + 1 << " ..."; std::this_thread::sleep_for(std::chrono::milliseconds(500)); - response = rpcClient(args, app().config(), app().logs(), apiVersion, headers); + response = rpcClient(args, app().config(), app().getLogs(), apiVersion, headers); } return response.second; diff --git a/src/test/jtx/impl/JSONRPCClient.cpp b/src/test/jtx/impl/JSONRPCClient.cpp index df41ed39be..6b60744aad 100644 --- a/src/test/jtx/impl/JSONRPCClient.cpp +++ b/src/test/jtx/impl/JSONRPCClient.cpp @@ -35,13 +35,15 @@ class JSONRPCClient : public AbstractClient continue; using namespace boost::asio::ip; if (pp.ip && pp.ip->is_unspecified()) + { *pp.ip = pp.ip->is_v6() ? address{address_v6::loopback()} : address{address_v4::loopback()}; + } if (!pp.port) Throw("Use fixConfigPorts with auto ports"); - return {*pp.ip, *pp.port}; + return {*pp.ip, *pp.port}; // NOLINT(bugprone-unchecked-optional-access) } Throw("Missing HTTP port"); return {}; // Silence compiler control paths return value warning diff --git a/src/test/jtx/impl/Oracle.cpp b/src/test/jtx/impl/Oracle.cpp index 24f7312e73..c692664b93 100644 --- a/src/test/jtx/impl/Oracle.cpp +++ b/src/test/jtx/impl/Oracle.cpp @@ -12,13 +12,13 @@ namespace test { namespace jtx { namespace oracle { -Oracle::Oracle(Env& env, CreateArg const& arg, bool submit) : env_(env), owner_{}, documentID_{} +Oracle::Oracle(Env& env, CreateArg const& arg, bool submit) : env_(env) { // LastUpdateTime is checked to be in range // {close-maxLastUpdateTimeDelta, close+maxLastUpdateTimeDelta}. // To make the validation work and to make the clock consistent // for tests running at different time, simulate Unix time starting - // on testStartTime since Ripple epoch. + // on testStartTime since XRPL epoch. auto const now = env_.timeKeeper().now(); if (now.time_since_epoch().count() == 0 || arg.close) env_.close(now + testStartTime - epoch_offset); @@ -38,11 +38,17 @@ Oracle::remove(RemoveArg const& arg) jv[jss::Account] = to_string(arg.owner.value_or(owner_)); toJson(jv[jss::OracleDocumentID], arg.documentID.value_or(documentID_)); if (Oracle::fee != 0) + { jv[jss::Fee] = std::to_string(Oracle::fee); + } else if (arg.fee != 0) + { jv[jss::Fee] = std::to_string(arg.fee); + } else + { jv[jss::Fee] = std::to_string(env_.current()->fees().increment.drops()); + } if (arg.flags != 0) jv[jss::Flags] = arg.flags; submit(jv, arg.msig, arg.seq, arg.err); @@ -58,22 +64,38 @@ Oracle::submit( if (msig) { if (seq && err) + { env_(jv, *msig, *seq, *err); + } else if (seq) + { env_(jv, *msig, *seq); + } else if (err) + { env_(jv, *msig, *err); + } else + { env_(jv, *msig); + } } else if (seq && err) + { env_(jv, *seq, *err); + } else if (seq) + { env_(jv, *seq); + } else if (err) + { env_(jv, *err); + } else + { env_(jv); + } env_.close(); } @@ -90,7 +112,7 @@ Oracle::expectPrice(DataSeries const& series) const if (auto const sle = env_.le(keylet::oracle(owner_, documentID_))) { auto const& leSeries = sle->getFieldArray(sfPriceDataSeries); - if (leSeries.size() == 0 || leSeries.size() != series.size()) + if (leSeries.empty() || leSeries.size() != series.size()) return false; for (auto const& data : series) { @@ -157,9 +179,13 @@ Oracle::aggregatePrice( if (jr.isObject()) { if (jr.isMember(jss::result) && jr[jss::result].isMember(jss::status)) + { return jr[jss::result]; - else if (jr.isMember(jss::error)) + } + if (jr.isMember(jss::error)) + { return jr; + } } return Json::nullValue; } @@ -177,9 +203,13 @@ Oracle::set(UpdateArg const& arg) jv[jss::OracleDocumentID] = documentID_; } else if (arg.documentID) + { toJson(jv[jss::OracleDocumentID], *arg.documentID); + } else + { jv[jss::OracleDocumentID] = documentID_; + } jv[jss::TransactionType] = jss::OracleSet; jv[jss::Account] = to_string(owner_); if (arg.assetClass) @@ -191,24 +221,36 @@ Oracle::set(UpdateArg const& arg) if (arg.flags != 0) jv[jss::Flags] = arg.flags; if (Oracle::fee != 0) + { jv[jss::Fee] = std::to_string(Oracle::fee); + } else if (arg.fee != 0) + { jv[jss::Fee] = std::to_string(arg.fee); + } else + { jv[jss::Fee] = std::to_string(env_.current()->fees().increment.drops()); + } // lastUpdateTime if provided is offset from testStartTime if (arg.lastUpdateTime) { if (std::holds_alternative(*arg.lastUpdateTime)) + { jv[jss::LastUpdateTime] = to_string(testStartTime.count() + std::get(*arg.lastUpdateTime)); + } else + { toJson(jv[jss::LastUpdateTime], *arg.lastUpdateTime); + } } else + { jv[jss::LastUpdateTime] = to_string( duration_cast(env_.current()->header().closeTime.time_since_epoch()).count() + epoch_offset.count()); + } Json::Value dataSeries(Json::arrayValue); auto assetToStr = [](std::string const& s) { // assume standard currency @@ -216,7 +258,7 @@ Oracle::set(UpdateArg const& arg) return s; assert(s.size() <= 20); // anything else must be 160-bit hex string - return strHex(s).append(40 - s.size() * 2, '0'); + return strHex(s).append(40 - (s.size() * 2), '0'); }; for (auto const& data : arg.series) { @@ -225,9 +267,12 @@ Oracle::set(UpdateArg const& arg) price[jss::BaseAsset] = assetToStr(std::get<0>(data)); price[jss::QuoteAsset] = assetToStr(std::get<1>(data)); if (std::get<2>(data)) - price[jss::AssetPrice] = *std::get<2>(data); + { + price[jss::AssetPrice] = + *std::get<2>(data); // NOLINT(bugprone-unchecked-optional-access) + } if (std::get<3>(data)) - price[jss::Scale] = *std::get<3>(data); + price[jss::Scale] = *std::get<3>(data); // NOLINT(bugprone-unchecked-optional-access) priceData[jss::PriceData] = price; dataSeries.append(priceData); } @@ -264,19 +309,27 @@ Oracle::ledgerEntry( if (account) { if (std::holds_alternative(*account)) + { jvParams[jss::oracle][jss::account] = to_string(std::get(*account)); + } else + { jvParams[jss::oracle][jss::account] = std::get(*account); + } } if (documentID) toJson(jvParams[jss::oracle][jss::oracle_document_id], *documentID); if (index) { - std::uint32_t i; + std::uint32_t i = 0; if (boost::conversion::try_lexical_convert(*index, i)) + { jvParams[jss::oracle][jss::ledger_index] = i; + } else + { jvParams[jss::oracle][jss::ledger_index] = *index; + } } // Convert "%None%" to None auto str = to_string(jvParams); @@ -307,12 +360,18 @@ toJsonHex(Json::Value& jv, AnyValue const& v) if constexpr (std::is_same_v) { if (arg.starts_with("##")) + { jv = arg.substr(2); + } else + { jv = strHex(arg); + } } else + { jv = arg; + } }, v); } diff --git a/src/test/jtx/impl/TestHelpers.cpp b/src/test/jtx/impl/TestHelpers.cpp index 5e7a31adcd..e10a0c46d0 100644 --- a/src/test/jtx/impl/TestHelpers.cpp +++ b/src/test/jtx/impl/TestHelpers.cpp @@ -2,6 +2,7 @@ #include #include +#include #include namespace xrpl { diff --git a/src/test/jtx/impl/WSClient.cpp b/src/test/jtx/impl/WSClient.cpp index 84424be222..2c3389c131 100644 --- a/src/test/jtx/impl/WSClient.cpp +++ b/src/test/jtx/impl/WSClient.cpp @@ -27,7 +27,7 @@ class WSClientImpl : public WSClient { Json::Value jv; - explicit msg(Json::Value&& jv_) : jv(jv_) + explicit msg(Json::Value&& jv_) : jv(std::move(jv_)) { } }; @@ -49,13 +49,15 @@ class WSClientImpl : public WSClient continue; using namespace boost::asio::ip; if (pp.ip && pp.ip->is_unspecified()) + { *pp.ip = pp.ip->is_v6() ? address{address_v6::loopback()} : address{address_v4::loopback()}; + } if (!pp.port) Throw("Use fixConfigPorts with auto ports"); - return {*pp.ip, *pp.port}; + return {*pp.ip, *pp.port}; // NOLINT(bugprone-unchecked-optional-access) } Throw("Missing WebSocket port"); return {}; // Silence compiler control paths return value warning @@ -178,9 +180,18 @@ public: jp[jss::id] = 5; } else + { jp[jss::command] = cmd; + } auto const s = to_string(jp); - ws_.write_some(true, buffer(s)); + + // Use the error_code overload to avoid an unhandled exception + // when the server closes the WebSocket connection (e.g. after + // booting a client that exceeded resource thresholds). + error_code ec; + ws_.write_some(true, buffer(s), ec); + if (ec) + return {}; } auto jv = @@ -268,7 +279,7 @@ private: rb_.consume(rb_.size()); auto m = std::make_shared(std::move(jv)); { - std::lock_guard lock(m_); + std::lock_guard const lock(m_); msgs_.push_front(m); cv_.notify_all(); } @@ -282,7 +293,7 @@ private: void on_read_done() { - std::lock_guard lock(m0_); + std::lock_guard const lock(m0_); b0_ = true; cv0_.notify_all(); } diff --git a/src/test/jtx/impl/amount.cpp b/src/test/jtx/impl/amount.cpp index 4952334fe8..25fcb5c8f0 100644 --- a/src/test/jtx/impl/amount.cpp +++ b/src/test/jtx/impl/amount.cpp @@ -62,9 +62,13 @@ operator<<(std::ostream& os, PrettyAmount const& amount) if (n < c) { if (amount.value().negative()) + { os << "-" << n << " drops"; + } else + { os << n << " drops"; + } return os; } auto const d = double(n) / dropsPerXRP.drops(); diff --git a/src/test/jtx/impl/balance.cpp b/src/test/jtx/impl/balance.cpp index a136b19bdb..1c3cc0b3c3 100644 --- a/src/test/jtx/impl/balance.cpp +++ b/src/test/jtx/impl/balance.cpp @@ -66,7 +66,7 @@ doBalance( void balance::operator()(Env& env) const { - return std::visit( + std::visit( [&](auto const& issue) { doBalance(env, account_.id(), none_, value_, issue); }, value_.asset().value()); } diff --git a/src/test/jtx/impl/batch.cpp b/src/test/jtx/impl/batch.cpp index 3f993ddea1..65aca2a935 100644 --- a/src/test/jtx/impl/batch.cpp +++ b/src/test/jtx/impl/batch.cpp @@ -74,6 +74,7 @@ sig::operator()(Env& env, JTx& jt) const Serializer msg; serializeBatch(msg, stx.getFlags(), stx.getBatchTransactionIDs()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const sig = xrpl::sign(*publicKeyType(e.sig.pk().slice()), e.sig.sk(), msg.slice()); jo[sfTxnSignature.getJsonName()] = strHex(Slice{sig.data(), sig.size()}); } @@ -112,6 +113,7 @@ msig::operator()(Env& env, JTx& jt) const Serializer msg; serializeBatch(msg, stx.getFlags(), stx.getBatchTransactionIDs()); finishMultiSigningData(e.acct.id(), msg); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const sig = xrpl::sign(*publicKeyType(e.sig.pk().slice()), e.sig.sk(), msg.slice()); iso[sfTxnSignature.getJsonName()] = strHex(Slice{sig.data(), sig.size()}); } diff --git a/src/test/jtx/impl/directory.cpp b/src/test/jtx/impl/directory.cpp index 7231389d1e..da0a338e7c 100644 --- a/src/test/jtx/impl/directory.cpp +++ b/src/test/jtx/impl/directory.cpp @@ -15,7 +15,7 @@ bumpLastPage( std::function adjust) -> Expected { Expected res{}; - env.app().openLedger().modify([&](OpenView& view, beast::Journal j) -> bool { + env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) -> bool { Sandbox sb(&view, tapNONE); // Find the root page @@ -72,7 +72,9 @@ bumpLastPage( // Adjust root previous and previous node's next sleRoot->setFieldU64(sfIndexPrevious, newLastPage); if (prevIndex.value_or(0) == 0) + { sleRoot->setFieldU64(sfIndexNext, newLastPage); + } else { auto slePrev = sb.peek(keylet::page(directory, *prevIndex)); @@ -88,6 +90,7 @@ bumpLastPage( // Fixup page numbers in the objects referred by indexes if (adjust) + { for (auto const key : indexes) { if (!adjust(sb, key, newLastPage)) @@ -96,6 +99,7 @@ bumpLastPage( return false; } } + } sb.apply(view); return true; diff --git a/src/test/jtx/impl/fee.cpp b/src/test/jtx/impl/fee.cpp index 2b1ac6c398..fc05afcb46 100644 --- a/src/test/jtx/impl/fee.cpp +++ b/src/test/jtx/impl/fee.cpp @@ -14,9 +14,13 @@ fee::operator()(Env& env, JTx& jt) const jt.fill_fee = false; assert(!increment_ || !amount_); if (increment_) + { jt[sfFee] = STAmount(env.current()->fees().increment).getJson(); + } else if (amount_) + { jt[sfFee] = amount_->getJson(JsonOptions::none); + } } } // namespace jtx diff --git a/src/test/jtx/impl/flags.cpp b/src/test/jtx/impl/flags.cpp index 11e7831f55..aee01a107e 100644 --- a/src/test/jtx/impl/flags.cpp +++ b/src/test/jtx/impl/flags.cpp @@ -24,11 +24,17 @@ flags::operator()(Env& env) const { auto const sle = env.le(account_); if (!sle) + { env.test.fail(); + } else if (sle->isFieldPresent(sfFlags)) + { env.test.expect((sle->getFieldU32(sfFlags) & mask_) == mask_); + } else + { env.test.expect(mask_ == 0); + } } void @@ -36,11 +42,17 @@ nflags::operator()(Env& env) const { auto const sle = env.le(account_); if (!sle) + { env.test.fail(); + } else if (sle->isFieldPresent(sfFlags)) + { env.test.expect((sle->getFieldU32(sfFlags) & mask_) == 0); + } else + { env.test.pass(); + } } } // namespace jtx diff --git a/src/test/jtx/impl/mpt.cpp b/src/test/jtx/impl/mpt.cpp index 4745b1a7a7..0a6af63450 100644 --- a/src/test/jtx/impl/mpt.cpp +++ b/src/test/jtx/impl/mpt.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -44,7 +45,7 @@ MPTTester::MPTTester(Env& env, Account const& issuer, MPTInit const& arg) if (arg.fund) { env_.fund(arg.xrp, issuer_); - for (auto it : holders_) + for (auto const& it : holders_) env_.fund(arg.xrpHolders, it.second); } if (close_) @@ -52,7 +53,7 @@ MPTTester::MPTTester(Env& env, Account const& issuer, MPTInit const& arg) if (arg.fund) { env_.require(owners(issuer_, 0)); - for (auto it : holders_) + for (auto const& it : holders_) { if (issuer_.id() == it.second.id()) Throw("Issuer can't be holder"); @@ -77,12 +78,14 @@ static MPTCreate makeMPTCreate(MPTInitDef const& arg) { if (arg.pay) + { return { .maxAmt = arg.maxAmt, .transferFee = arg.transferFee, .pay = {{arg.holders, *arg.pay}}, .flags = arg.flags, .authHolder = arg.authHolder}; + } return { .maxAmt = arg.maxAmt, .transferFee = arg.transferFee, @@ -137,7 +140,7 @@ MPTTester::create(MPTCreate const& arg) if (id_) Throw("MPT can't be reused"); id_ = makeMptID(env_.seq(issuer_), issuer_); - Json::Value jv = createJV( + Json::Value const jv = createJV( {.issuer = issuer_, .maxAmt = arg.maxAmt, .assetScale = arg.assetScale, @@ -145,7 +148,7 @@ MPTTester::create(MPTCreate const& arg) .metadata = arg.metadata, .mutableFlags = arg.mutableFlags, .domainID = arg.domainID}); - if (submit(arg, jv) != tesSUCCESS) + if (!isTesSuccess(submit(arg, jv))) { // Verify issuance doesn't exist env_.require( @@ -174,16 +177,24 @@ MPTTester::create(MPTCreate const& arg) if (arg.authorize) { if (arg.authorize->empty()) + { authAndPay(holders_, [](auto const& it) { return it.second; }); + } else + { authAndPay(*arg.authorize, [](auto const& it) { return it; }); + } } else if (arg.pay) { if (arg.pay->first.empty()) + { authAndPay(holders_, [](auto const& it) { return it.second; }); + } else + { authAndPay(arg.pay->first, [](auto const& it) { return it; }); + } } } } @@ -206,7 +217,7 @@ MPTTester::destroy(MPTDestroy const& arg) { if (!arg.id && !id_) Throw("MPT has not been created"); - Json::Value jv = + Json::Value const jv = destroyJV({.issuer = arg.issuer ? arg.issuer : issuer_, .id = arg.id ? arg.id : id_}); submit(arg, jv); } @@ -240,12 +251,12 @@ MPTTester::authorize(MPTAuthorize const& arg) { if (!arg.id && !id_) Throw("MPT has not been created"); - Json::Value jv = authorizeJV({ + Json::Value const jv = authorizeJV({ .account = arg.account ? arg.account : issuer_, .holder = arg.holder, .id = arg.id ? arg.id : id_, }); - if (auto const result = submit(arg, jv); result == tesSUCCESS) + if (auto const result = submit(arg, jv); isTesSuccess(result)) { // Issuer authorizes if (!arg.account || *arg.account == issuer_) @@ -253,10 +264,14 @@ MPTTester::authorize(MPTAuthorize const& arg) auto const flags = getFlags(arg.holder); // issuer un-authorizes the holder if (arg.flags.value_or(0) == tfMPTUnauthorize) + { env_.require(mptflags(*this, flags, arg.holder)); - // issuer authorizes the holder + // issuer authorizes the holder + } else + { env_.require(mptflags(*this, flags | lsfMPTAuthorized, arg.holder)); + } } // Holder authorizes else if (arg.flags.value_or(0) != tfMPTUnauthorize) @@ -315,9 +330,13 @@ MPTTester::setJV(MPTSet const& arg) std::visit( [&jv](T const& holder) { if constexpr (std::is_same_v) + { jv[sfHolder] = holder.human(); + } else if constexpr (std::is_same_v) + { jv[sfHolder] = toBase58(holder); + } }, *arg.holder); } @@ -342,7 +361,7 @@ MPTTester::set(MPTSet const& arg) { if (!arg.id && !id_) Throw("MPT has not been created"); - Json::Value jv = setJV( + Json::Value const jv = setJV( {.account = arg.account ? arg.account : issuer_, .holder = arg.holder, .id = arg.id ? arg.id : id_, @@ -351,7 +370,7 @@ MPTTester::set(MPTSet const& arg) .metadata = arg.metadata, .delegate = arg.delegate, .domainID = arg.domainID}); - if (submit(arg, jv) == tesSUCCESS && (arg.flags.value_or(0) || arg.mutableFlags)) + if (submit(arg, jv) == tesSUCCESS && ((arg.flags.value_or(0) != 0u) || arg.mutableFlags)) { auto require = [&](std::optional const& holder, bool unchanged) { auto flags = getFlags(holder); @@ -360,42 +379,70 @@ MPTTester::set(MPTSet const& arg) if (arg.flags) { if (*arg.flags & tfMPTLock) + { flags |= lsfMPTLocked; + } else if (*arg.flags & tfMPTUnlock) + { flags &= ~lsfMPTLocked; + } } if (arg.mutableFlags) { if (*arg.mutableFlags & tmfMPTSetCanLock) + { flags |= lsfMPTCanLock; + } else if (*arg.mutableFlags & tmfMPTClearCanLock) + { flags &= ~lsfMPTCanLock; + } if (*arg.mutableFlags & tmfMPTSetRequireAuth) + { flags |= lsfMPTRequireAuth; + } else if (*arg.mutableFlags & tmfMPTClearRequireAuth) + { flags &= ~lsfMPTRequireAuth; + } if (*arg.mutableFlags & tmfMPTSetCanEscrow) + { flags |= lsfMPTCanEscrow; + } else if (*arg.mutableFlags & tmfMPTClearCanEscrow) + { flags &= ~lsfMPTCanEscrow; + } if (*arg.mutableFlags & tmfMPTSetCanClawback) + { flags |= lsfMPTCanClawback; + } else if (*arg.mutableFlags & tmfMPTClearCanClawback) + { flags &= ~lsfMPTCanClawback; + } if (*arg.mutableFlags & tmfMPTSetCanTrade) + { flags |= lsfMPTCanTrade; + } else if (*arg.mutableFlags & tmfMPTClearCanTrade) + { flags &= ~lsfMPTCanTrade; + } if (*arg.mutableFlags & tmfMPTSetCanTransfer) + { flags |= lsfMPTCanTransfer; + } else if (*arg.mutableFlags & tmfMPTClearCanTransfer) + { flags &= ~lsfMPTCanTransfer; + } } } env_.require(mptflags(*this, flags, holder)); @@ -498,14 +545,18 @@ MPTTester::pay( auto const outstandingAmt = getBalance(issuer_); if (credentials) + { env_( jtx::pay(src, dest, mpt(amount)), ter(err.value_or(tesSUCCESS)), credentials::ids(*credentials)); + } else + { env_(jtx::pay(src, dest, mpt(amount)), ter(err.value_or(tesSUCCESS))); + } - if (env_.ter() != tesSUCCESS) + if (!isTesSuccess(env_.ter())) amount = 0; if (close_) env_.close(); @@ -543,7 +594,7 @@ MPTTester::claw( auto const issuerAmt = getBalance(issuer); auto const holderAmt = getBalance(holder); env_(jtx::claw(issuer, mpt(amount), holder), ter(err.value_or(tesSUCCESS))); - if (env_.ter() != tesSUCCESS) + if (!isTesSuccess(env_.ter())) amount = 0; if (close_) env_.close(); diff --git a/src/test/jtx/impl/multisign.cpp b/src/test/jtx/impl/multisign.cpp index 54bde3ebb9..8e3c37f68c 100644 --- a/src/test/jtx/impl/multisign.cpp +++ b/src/test/jtx/impl/multisign.cpp @@ -53,9 +53,13 @@ msig::operator()(Env& env, JTx& jt) const // The signing pub key is only required at the top level. if (!subField) + { sigObject[sfSigningPubKey] = ""; + } else if (sigObject.isNull()) + { sigObject = Json::Value(Json::objectValue); + } std::optional st; try { @@ -74,15 +78,19 @@ msig::operator()(Env& env, JTx& jt) const jo[jss::Account] = e.acct.human(); jo[jss::SigningPubKey] = strHex(e.sig.pk().slice()); - Serializer ss{buildMultiSigningData(*st, e.acct.id())}; + Serializer const ss{buildMultiSigningData(*st, e.acct.id())}; auto const sig = xrpl::sign(*publicKeyType(e.sig.pk().slice()), e.sig.sk(), ss.slice()); jo[sfTxnSignature.getJsonName()] = strHex(Slice{sig.data(), sig.size()}); } }; - if (!subField) + if (subField == nullptr) + { jt.mainSigners.emplace_back(callback); + } else + { jt.postSigners.emplace_back(callback); + } } } // namespace jtx diff --git a/src/test/jtx/impl/offer.cpp b/src/test/jtx/impl/offer.cpp index 251b659f3b..5a2264601a 100644 --- a/src/test/jtx/impl/offer.cpp +++ b/src/test/jtx/impl/offer.cpp @@ -17,7 +17,7 @@ offer( jv[jss::Account] = account.human(); jv[jss::TakerPays] = takerPays.getJson(JsonOptions::none); jv[jss::TakerGets] = takerGets.getJson(JsonOptions::none); - if (flags) + if (flags != 0u) jv[jss::Flags] = flags; jv[jss::TransactionType] = jss::OfferCreate; return jv; diff --git a/src/test/jtx/impl/owners.cpp b/src/test/jtx/impl/owners.cpp index 53f2302143..855c5b04ff 100644 --- a/src/test/jtx/impl/owners.cpp +++ b/src/test/jtx/impl/owners.cpp @@ -1,5 +1,7 @@ #include +#include + namespace xrpl { namespace detail { diff --git a/src/test/jtx/impl/paths.cpp b/src/test/jtx/impl/paths.cpp index e26f214fe4..af72b8bd5a 100644 --- a/src/test/jtx/impl/paths.cpp +++ b/src/test/jtx/impl/paths.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include @@ -30,7 +30,7 @@ paths::operator()(Env& env, JTx& jt) const } Pathfinder pf( - std::make_shared(env.current(), env.app().journal("RippleLineCache")), + std::make_shared(env.current(), env.app().getJournal("RippleLineCache")), from, to, in_.currency, diff --git a/src/test/jtx/impl/permissioned_dex.cpp b/src/test/jtx/impl/permissioned_dex.cpp index 87b7896663..494ea897d4 100644 --- a/src/test/jtx/impl/permissioned_dex.cpp +++ b/src/test/jtx/impl/permissioned_dex.cpp @@ -20,7 +20,7 @@ setupDomain( env.fund(XRP(100000), domainOwner); env.close(); - pdomain::Credentials credentials{{domainOwner, credType}}; + pdomain::Credentials const credentials{{domainOwner, 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 fffcaa91a1..60d653e956 100644 --- a/src/test/jtx/impl/permissioned_domains.cpp +++ b/src/test/jtx/impl/permissioned_domains.cpp @@ -109,6 +109,7 @@ credentialsFromJson( obj = credential[jss::Credential]; auto const& issuer = obj[jss::Issuer]; 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())}); } diff --git a/src/test/jtx/impl/quality2.cpp b/src/test/jtx/impl/quality2.cpp index c202592b9d..9da366d4c2 100644 --- a/src/test/jtx/impl/quality2.cpp +++ b/src/test/jtx/impl/quality2.cpp @@ -8,12 +8,14 @@ namespace test { namespace jtx { qualityInPercent::qualityInPercent(double percent) + // NOLINTNEXTLINE(cppcoreguidelines-use-default-member-init) : qIn_(static_cast((percent / 100) * QUALITY_ONE)) { assert(percent <= 400 && percent >= 0); } qualityOutPercent::qualityOutPercent(double percent) + // NOLINTNEXTLINE(cppcoreguidelines-use-default-member-init) : qOut_(static_cast((percent / 100) * QUALITY_ONE)) { assert(percent <= 400 && percent >= 0); diff --git a/src/test/jtx/impl/sig.cpp b/src/test/jtx/impl/sig.cpp index 3ea7f669a7..1bdadc0bd3 100644 --- a/src/test/jtx/impl/sig.cpp +++ b/src/test/jtx/impl/sig.cpp @@ -10,7 +10,7 @@ sig::operator()(Env&, JTx& jt) const { if (!manual_) return; - if (!subField_) + if (subField_ == nullptr) jt.fill_sig = false; if (account_) { @@ -22,10 +22,14 @@ sig::operator()(Env&, JTx& jt) const jtx::sign(jtx.jv, account, sigObject); }; - if (!subField_) + if (subField_ == nullptr) + { jt.mainSigners.emplace_back(callback); + } else + { jt.postSigners.emplace_back(callback); + } } } diff --git a/src/test/jtx/impl/testline.cpp b/src/test/jtx/impl/testline.cpp deleted file mode 100644 index fbcb1d2de7..0000000000 --- a/src/test/jtx/impl/testline.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include - -namespace xrpl { -namespace test { -namespace jtx { - -void -testline::operator()(Env&, JTx& jt) const -{ - jt.testLine = line_; -} - -} // namespace jtx -} // namespace test -} // namespace xrpl diff --git a/src/test/jtx/impl/vault.cpp b/src/test/jtx/impl/vault.cpp index 90250aece0..49c0dddaec 100644 --- a/src/test/jtx/impl/vault.cpp +++ b/src/test/jtx/impl/vault.cpp @@ -12,7 +12,7 @@ namespace test { namespace jtx { std::tuple -Vault::create(CreateArgs const& args) +Vault::create(CreateArgs const& args) const { auto keylet = keylet::vault(args.owner.id(), env.seq(args.owner)); Json::Value jv; diff --git a/src/test/jtx/impl/xchain_bridge.cpp b/src/test/jtx/impl/xchain_bridge.cpp index dfd7f0ba96..72d932463f 100644 --- a/src/test/jtx/impl/xchain_bridge.cpp +++ b/src/test/jtx/impl/xchain_bridge.cpp @@ -286,6 +286,7 @@ claim_attestations( JValueVec vec; vec.reserve(numAtts); for (auto i = fromIdx; i < fromIdx + numAtts; ++i) + { vec.emplace_back(claim_attestation( submittingAccount, jvBridge, @@ -296,6 +297,7 @@ claim_attestations( claimID, dst, signers[i])); + } return vec; } @@ -319,6 +321,7 @@ create_account_attestations( JValueVec vec; vec.reserve(numAtts); for (auto i = fromIdx; i < fromIdx + numAtts; ++i) + { vec.emplace_back(create_account_attestation( submittingAccount, jvBridge, @@ -330,6 +333,7 @@ create_account_attestations( createCount, dst, signers[i])); + } return vec; } @@ -409,7 +413,6 @@ XChainBridgeObjects::XChainBridgeObjects() } return r; }()) - , quorum(UT_XCHAIN_DEFAULT_QUORUM) , reward(XRP(1)) , split_reward_quorum(divide(reward, STAmount(UT_XCHAIN_DEFAULT_QUORUM), reward.issue())) , split_reward_everyone(divide(reward, STAmount(UT_XCHAIN_DEFAULT_NUM_SIGNERS), reward.issue())) @@ -427,7 +430,7 @@ XChainBridgeObjects::XChainBridgeObjects() void XChainBridgeObjects::createMcBridgeObjects(Env& mcEnv) { - STAmount xrp_funds{XRP(10000)}; + STAmount const xrp_funds{XRP(10000)}; mcEnv.fund(xrp_funds, mcDoor, mcAlice, mcBob, mcCarol, mcGw); // Signer's list must match the attestation signers @@ -444,7 +447,7 @@ XChainBridgeObjects::createMcBridgeObjects(Env& mcEnv) void XChainBridgeObjects::createScBridgeObjects(Env& scEnv) { - STAmount xrp_funds{XRP(10000)}; + STAmount const xrp_funds{XRP(10000)}; scEnv.fund(xrp_funds, scDoor, scAlice, scBob, scCarol, scGw, scAttester, scReward); // Signer's list must match the attestation signers diff --git a/src/test/jtx/mpt.h b/src/test/jtx/mpt.h index 3368d129c1..3990dd3087 100644 --- a/src/test/jtx/mpt.h +++ b/src/test/jtx/mpt.h @@ -291,7 +291,7 @@ private: env_.require(owners(issuer_, *arg.ownerCount)); if (arg.holderCount) { - for (auto it : holders_) + for (auto const& it : holders_) env_.require(owners(it.second, *arg.holderCount)); } return err; diff --git a/src/test/jtx/multisign.h b/src/test/jtx/multisign.h index 56d0b10906..b3f38cc453 100644 --- a/src/test/jtx/multisign.h +++ b/src/test/jtx/multisign.h @@ -22,7 +22,7 @@ struct signer std::optional tag; signer(Account account_, std::uint32_t weight_ = 1, std::optional tag_ = std::nullopt) - : weight(weight_), account(std::move(account_)), tag(std::move(tag_)) + : weight(weight_), account(std::move(account_)), tag(tag_) { } }; diff --git a/src/test/jtx/owners.h b/src/test/jtx/owners.h index 785a9347dd..3f54e0c55f 100644 --- a/src/test/jtx/owners.h +++ b/src/test/jtx/owners.h @@ -2,7 +2,7 @@ #include -#include +#include #include #include diff --git a/src/test/jtx/quality.h b/src/test/jtx/quality.h index 34c960224f..2da83eeef5 100644 --- a/src/test/jtx/quality.h +++ b/src/test/jtx/quality.h @@ -25,7 +25,7 @@ public: class qualityInPercent { private: - std::uint32_t qIn_; + std::uint32_t qIn_; // NOLINT(cppcoreguidelines-use-default-member-init) public: explicit qualityInPercent(double percent); @@ -53,7 +53,7 @@ public: class qualityOutPercent { private: - std::uint32_t qOut_; + std::uint32_t qOut_; // NOLINT(cppcoreguidelines-use-default-member-init) public: explicit qualityOutPercent(double percent); diff --git a/src/test/jtx/testline.h b/src/test/jtx/testline.h deleted file mode 100644 index e2be1f0276..0000000000 --- a/src/test/jtx/testline.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include - -namespace xrpl { -namespace test { -namespace jtx { - -/** Store the line number of the current test in a JTx. - - Intended to help debug failing transaction submission tests. -*/ -class testline -{ -private: - int line_; - -public: - explicit testline(int line) : line_(line) - { - } - - void - operator()(Env&, JTx& jt) const; -}; - -#define THISLINE testline(__LINE__) - -} // namespace jtx -} // namespace test -} // namespace xrpl diff --git a/src/test/jtx/utility.h b/src/test/jtx/utility.h index 895d00f12b..c9189efc36 100644 --- a/src/test/jtx/utility.h +++ b/src/test/jtx/utility.h @@ -2,9 +2,8 @@ #include -#include - #include +#include #include #include @@ -50,7 +49,7 @@ fill_fee(Json::Value& jv, ReadView const& view); void fill_seq(Json::Value& jv, ReadView const& view); -/** Given a rippled unit test rpc command, return the corresponding JSON. */ +/** Given an xrpld unit test rpc command, return the corresponding JSON. */ Json::Value cmdToJSONRPC(std::vector const& args, beast::Journal j, unsigned int apiVersion); diff --git a/src/test/jtx/vault.h b/src/test/jtx/vault.h index 65a5706354..748d3341a5 100644 --- a/src/test/jtx/vault.h +++ b/src/test/jtx/vault.h @@ -30,7 +30,7 @@ struct Vault /** Return a VaultCreate transaction and the Vault's expected keylet. */ std::tuple - create(CreateArgs const& args); + create(CreateArgs const& args) const; struct SetArgs { @@ -38,7 +38,7 @@ struct Vault uint256 id; }; - Json::Value + static Json::Value set(SetArgs const& args); struct DeleteArgs @@ -47,7 +47,7 @@ struct Vault uint256 id; }; - Json::Value + static Json::Value del(DeleteArgs const& args); struct DepositArgs @@ -57,7 +57,7 @@ struct Vault STAmount amount; }; - Json::Value + static Json::Value deposit(DepositArgs const& args); struct WithdrawArgs @@ -67,7 +67,7 @@ struct Vault STAmount amount; }; - Json::Value + static Json::Value withdraw(WithdrawArgs const& args); struct ClawbackArgs @@ -78,7 +78,7 @@ struct Vault std::optional amount{}; }; - Json::Value + static Json::Value clawback(ClawbackArgs const& args); }; diff --git a/src/test/jtx/xchain_bridge.h b/src/test/jtx/xchain_bridge.h index 698426c59c..1270d03ed6 100644 --- a/src/test/jtx/xchain_bridge.h +++ b/src/test/jtx/xchain_bridge.h @@ -170,7 +170,7 @@ struct XChainBridgeObjects std::vector const alt_signers; std::vector const payee; std::vector const payees; - std::uint32_t const quorum; + std::uint32_t const quorum{UT_XCHAIN_DEFAULT_QUORUM}; STAmount const reward; // 1 xrp STAmount const split_reward_quorum; // 250,000 drops diff --git a/src/test/ledger/BookDirs_test.cpp b/src/test/ledger/BookDirs_test.cpp index 50f1bb46ba..e8733d159d 100644 --- a/src/test/ledger/BookDirs_test.cpp +++ b/src/test/ledger/BookDirs_test.cpp @@ -19,7 +19,7 @@ struct BookDirs_test : public beast::unit_test::suite env.close(); { - Book book(xrpIssue(), USD.issue(), std::nullopt); + Book const book(xrpIssue(), USD.issue(), std::nullopt); { auto d = BookDirs(*env.current(), book); BEAST_EXPECT(std::begin(d) == std::end(d)); @@ -56,8 +56,10 @@ struct BookDirs_test : public beast::unit_test::suite { auto AUD = gw["AUD"]; for (auto i = 1, j = 3; i <= 3; ++i, --j) + { for (auto k = 0; k < 80; ++k) env(offer("alice", AUD(i), XRP(j))); + } auto d = BookDirs(*env.current(), Book(AUD.issue(), xrpIssue(), std::nullopt)); BEAST_EXPECT(std::distance(d.begin(), d.end()) == 240); diff --git a/src/test/ledger/Directory_test.cpp b/src/test/ledger/Directory_test.cpp index 8e7ba38c53..4115d03c19 100644 --- a/src/test/ledger/Directory_test.cpp +++ b/src/test/ledger/Directory_test.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +37,7 @@ struct Directory_test : public beast::unit_test::suite // Insert n empty pages, numbered [0, ... n - 1], in the // specified directory: - void + static void makePages(Sandbox& sb, uint256 const& base, std::uint64_t n) { for (std::uint64_t i = 0; i < n; ++i) @@ -46,14 +47,22 @@ struct Directory_test : public beast::unit_test::suite p->setFieldV256(sfIndexes, STVector256{}); if (i + 1 == n) + { p->setFieldU64(sfIndexNext, 0); + } else + { p->setFieldU64(sfIndexNext, i + 1); + } if (i == 0) + { p->setFieldU64(sfIndexPrevious, n - 1); + } else + { p->setFieldU64(sfIndexPrevious, i - 1); + } sb.insert(p); } @@ -195,7 +204,7 @@ struct Directory_test : public beast::unit_test::suite BEAST_EXPECT(dirIsEmpty(*env.closed(), keylet::ownerDir(alice))); - for (auto c : currencies) + for (auto const& c : currencies) { env(trust(charlie, c(50))); env.close(); @@ -254,8 +263,10 @@ struct Directory_test : public beast::unit_test::suite // Fill up three pages of offers for (int i = 0; i < 3; ++i) + { for (int j = 0; j < dirNodeMaxEntries; ++j) env(offer(alice, XRP(1), USD(1))); + } env.close(); // remove all the offers. Remove the middle page last @@ -263,7 +274,7 @@ struct Directory_test : public beast::unit_test::suite { for (int i = 0; i < dirNodeMaxEntries; ++i) { - env(offer_cancel(alice, firstOfferSeq + page * dirNodeMaxEntries + i)); + env(offer_cancel(alice, firstOfferSeq + (page * dirNodeMaxEntries) + i)); env.close(); } } @@ -271,7 +282,7 @@ struct Directory_test : public beast::unit_test::suite // All the offers have been cancelled, so the book // should have no entries and be empty: { - Sandbox sb(env.closed().get(), tapNONE); + Sandbox const sb(env.closed().get(), tapNONE); uint256 const bookBase = getBookBase({xrpIssue(), USD.issue(), std::nullopt}); BEAST_EXPECT(dirIsEmpty(sb, keylet::page(bookBase))); diff --git a/src/test/ledger/PaymentSandbox_test.cpp b/src/test/ledger/PaymentSandbox_test.cpp index 4be5827630..69e2657d0c 100644 --- a/src/test/ledger/PaymentSandbox_test.cpp +++ b/src/test/ledger/PaymentSandbox_test.cpp @@ -2,7 +2,8 @@ #include #include -#include +#include +#include #include #include @@ -64,7 +65,7 @@ class PaymentSandbox_test : public beast::unit_test::suite env(offer(snd, USD_gw1(2), USD_gw2(2)), txflags(tfPassive)); env(offer(snd, USD_gw2(2), USD_gw1(2)), txflags(tfPassive)); - PathSet paths(Path(gw1, USD_gw2, gw2), Path(gw2, USD_gw1, gw1)); + PathSet const paths(Path(gw1, USD_gw2, gw2), Path(gw2, USD_gw1, gw1)); env(pay(snd, rcv, any(USD_gw1(4))), json(paths.json()), @@ -87,7 +88,7 @@ class PaymentSandbox_test : public beast::unit_test::suite env.fund(XRP(10000), alice, gw1, gw2); - auto j = env.app().journal("View"); + auto j = env.app().getJournal("View"); auto const USD_gw1 = gw1["USD"]; auto const USD_gw2 = gw2["USD"]; @@ -109,14 +110,14 @@ class PaymentSandbox_test : public beast::unit_test::suite accountHolds(av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); { auto r = accountSend(av, gw1, alice, toCredit, j); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } BEAST_EXPECT( accountHolds(av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount + toCredit); { auto r = accountSend(av, alice, gw1, toDebit, j); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } BEAST_EXPECT( accountHolds(av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == @@ -124,19 +125,19 @@ class PaymentSandbox_test : public beast::unit_test::suite } { - // rippleCredit, no deferredCredits + // directSendNoFee, no deferredCredits ApplyViewImpl av(&*env.current(), tapNONE); auto const iss = USD_gw1.issue(); auto const startingAmount = accountHolds(av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); - rippleCredit(av, gw1, alice, toCredit, true, j); + directSendNoFee(av, gw1, alice, toCredit, true, j); BEAST_EXPECT( accountHolds(av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount + toCredit); - rippleCredit(av, alice, gw1, toDebit, true, j); + directSendNoFee(av, alice, gw1, toDebit, true, j); BEAST_EXPECT( accountHolds(av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount + toCredit - toDebit); @@ -153,7 +154,7 @@ class PaymentSandbox_test : public beast::unit_test::suite { auto r = accountSend(pv, gw1, alice, toCredit, j); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } BEAST_EXPECT( accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == @@ -161,7 +162,7 @@ class PaymentSandbox_test : public beast::unit_test::suite { auto r = accountSend(pv, alice, gw1, toDebit, j); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } BEAST_EXPECT( accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == @@ -169,7 +170,7 @@ class PaymentSandbox_test : public beast::unit_test::suite } { - // rippleCredit, w/ deferredCredits + // directSendNoFee, w/ deferredCredits ApplyViewImpl av(&*env.current(), tapNONE); PaymentSandbox pv(&av); @@ -177,7 +178,7 @@ class PaymentSandbox_test : public beast::unit_test::suite auto const startingAmount = accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); - rippleCredit(pv, gw1, alice, toCredit, true, j); + directSendNoFee(pv, gw1, alice, toCredit, true, j); BEAST_EXPECT( accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount); @@ -224,7 +225,7 @@ class PaymentSandbox_test : public beast::unit_test::suite { auto r = accountSend(pv, gw1, alice, toCredit, j); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } BEAST_EXPECT( accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == @@ -237,7 +238,7 @@ class PaymentSandbox_test : public beast::unit_test::suite startingAmount); { auto r = accountSend(pv2, gw1, alice, toCredit, j); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } BEAST_EXPECT( accountHolds(pv2, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == @@ -246,7 +247,7 @@ class PaymentSandbox_test : public beast::unit_test::suite { auto r = accountSend(pv, alice, gw1, toDebit, j); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } BEAST_EXPECT( accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == @@ -265,16 +266,16 @@ class PaymentSandbox_test : public beast::unit_test::suite using namespace jtx; - Env env(*this, features); + Env const env(*this, features); Account const gw("gw"); Account const alice("alice"); auto const USD = gw["USD"]; auto const issue = USD.issue(); - STAmount tinyAmt( + STAmount const tinyAmt( issue, STAmount::cMinValue, STAmount::cMinOffset + 1, false, STAmount::unchecked{}); - STAmount hugeAmt( + STAmount const hugeAmt( issue, STAmount::cMaxValue, STAmount::cMaxOffset - 1, false, STAmount::unchecked{}); ApplyViewImpl av(&*env.current(), tapNONE); @@ -314,11 +315,11 @@ class PaymentSandbox_test : public beast::unit_test::suite { auto r = accountSend(sb, xrpAccount(), alice, XRP(100), env.journal); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } { auto r = accountSend(sb, alice, xrpAccount(), XRP(100), env.journal); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } BEAST_EXPECT(accountFundsXRP(sb, alice, env.journal) == beast::zero); } @@ -332,7 +333,7 @@ class PaymentSandbox_test : public beast::unit_test::suite testcase("balanceHook"); using namespace jtx; - Env env(*this, features); + Env const env(*this, features); Account const gw("gw"); auto const USD = gw["USD"]; diff --git a/src/test/ledger/PendingSaves_test.cpp b/src/test/ledger/PendingSaves_test.cpp index ddf371e913..5e08fc53e4 100644 --- a/src/test/ledger/PendingSaves_test.cpp +++ b/src/test/ledger/PendingSaves_test.cpp @@ -1,6 +1,5 @@ -#include - #include +#include namespace xrpl { namespace test { diff --git a/src/test/ledger/SkipList_test.cpp b/src/test/ledger/SkipList_test.cpp index 8430db3904..a2695bfce8 100644 --- a/src/test/ledger/SkipList_test.cpp +++ b/src/test/ledger/SkipList_test.cpp @@ -1,8 +1,7 @@ #include -#include - #include +#include #include namespace xrpl { @@ -16,13 +15,17 @@ class SkipList_test : public beast::unit_test::suite jtx::Env env(*this); std::vector> history; { - Config config; + Config const config; auto prev = std::make_shared( - create_genesis, config, std::vector{}, env.app().getNodeFamily()); + create_genesis, + Rules{config.features}, + config.FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); history.push_back(prev); for (auto i = 0; i < 1023; ++i) { - auto next = std::make_shared(*prev, env.app().timeKeeper().closeTime()); + auto next = std::make_shared(*prev, env.app().getTimeKeeper().closeTime()); next->updateSkipList(); history.push_back(next); prev = next; diff --git a/src/test/ledger/View_test.cpp b/src/test/ledger/View_test.cpp index 80ecdba2d5..d2d930732e 100644 --- a/src/test/ledger/View_test.cpp +++ b/src/test/ledger/View_test.cpp @@ -1,12 +1,14 @@ #include -#include #include #include +#include #include #include #include +#include +#include #include #include @@ -88,7 +90,7 @@ class View_test : public beast::unit_test::suite auto const next = v.succ(k(id).key); if (answer) { - if (BEAST_EXPECT(next)) + if (BEAST_EXPECT(next); next.has_value()) BEAST_EXPECT(*next == k(*answer).key); } else @@ -112,12 +114,17 @@ class View_test : public beast::unit_test::suite using namespace jtx; Env env(*this); - Config config; + Config const config; std::shared_ptr const genesis = std::make_shared( - create_genesis, config, std::vector{}, env.app().getNodeFamily()); - auto const ledger = std::make_shared(*genesis, env.app().timeKeeper().closeTime()); + create_genesis, + Rules{config.features}, + config.FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); + auto const ledger = + std::make_shared(*genesis, env.app().getTimeKeeper().closeTime()); wipe(*ledger); - ReadView& v = *ledger; + ReadView const& v = *ledger; succ(v, 0, std::nullopt); ledger->rawInsert(sle(1, 1)); BEAST_EXPECT(v.exists(k(1))); @@ -145,7 +152,7 @@ class View_test : public beast::unit_test::suite using namespace jtx; Env env(*this); - wipe(env.app().openLedger()); + wipe(env.app().getOpenLedger()); auto const open = env.current(); ApplyViewImpl v(&*open, tapNONE); succ(v, 0, std::nullopt); @@ -178,7 +185,7 @@ class View_test : public beast::unit_test::suite using namespace jtx; Env env(*this); - wipe(env.app().openLedger()); + wipe(env.app().getOpenLedger()); auto const open = env.current(); ApplyViewImpl v0(&*open, tapNONE); v0.insert(sle(1)); @@ -244,7 +251,7 @@ class View_test : public beast::unit_test::suite using namespace jtx; Env env(*this); - wipe(env.app().openLedger()); + wipe(env.app().getOpenLedger()); auto const open = env.current(); ApplyViewImpl v0(&*open, tapNONE); v0.rawInsert(sle(1, 1)); @@ -313,7 +320,7 @@ class View_test : public beast::unit_test::suite using namespace std::chrono; { Env env(*this); - wipe(env.app().openLedger()); + wipe(env.app().getOpenLedger()); auto const open = env.current(); OpenView v0(open.get()); BEAST_EXPECT(v0.seq() != 98); @@ -331,7 +338,7 @@ class View_test : public beast::unit_test::suite BEAST_EXPECT(v2.seq() == v1.seq()); BEAST_EXPECT(v2.flags() == tapRETRY); - Sandbox v3(&v2); + Sandbox const v3(&v2); BEAST_EXPECT(v3.seq() == v2.seq()); BEAST_EXPECT(v3.parentCloseTime() == v2.parentCloseTime()); BEAST_EXPECT(v3.flags() == tapRETRY); @@ -342,7 +349,7 @@ class View_test : public beast::unit_test::suite BEAST_EXPECT(v2.seq() == v0.seq()); BEAST_EXPECT(v2.parentCloseTime() == v0.parentCloseTime()); BEAST_EXPECT(v2.flags() == tapRETRY); - PaymentSandbox v3(&v2); + PaymentSandbox const v3(&v2); BEAST_EXPECT(v3.seq() == v2.seq()); BEAST_EXPECT(v3.parentCloseTime() == v2.parentCloseTime()); BEAST_EXPECT(v3.flags() == v2.flags()); @@ -375,10 +382,15 @@ class View_test : public beast::unit_test::suite using namespace jtx; Env env(*this); - Config config; + Config const config; std::shared_ptr const genesis = std::make_shared( - create_genesis, config, std::vector{}, env.app().getNodeFamily()); - auto const ledger = std::make_shared(*genesis, env.app().timeKeeper().closeTime()); + create_genesis, + Rules{config.features}, + config.FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); + auto const ledger = + std::make_shared(*genesis, env.app().getTimeKeeper().closeTime()); auto setup = [&ledger](std::vector const& vec) { wipe(*ledger); @@ -579,10 +591,15 @@ class View_test : public beast::unit_test::suite using namespace jtx; Env env(*this); - Config config; + Config const config; std::shared_ptr const genesis = std::make_shared( - create_genesis, config, std::vector{}, env.app().getNodeFamily()); - auto const ledger = std::make_shared(*genesis, env.app().timeKeeper().closeTime()); + create_genesis, + Rules{config.features}, + config.FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); + auto const ledger = + std::make_shared(*genesis, env.app().getTimeKeeper().closeTime()); auto setup123 = [&ledger, this]() { // erase middle element wipe(*ledger); @@ -925,14 +942,18 @@ class View_test : public beast::unit_test::suite // erase the item, apply. { Env env(*this); - Config config; + Config const config; std::shared_ptr const genesis = std::make_shared( - create_genesis, config, std::vector{}, env.app().getNodeFamily()); + create_genesis, + Rules{config.features}, + config.FEES.toFees(), + std::vector{}, + env.app().getNodeFamily()); auto const ledger = - std::make_shared(*genesis, env.app().timeKeeper().closeTime()); + std::make_shared(*genesis, env.app().getTimeKeeper().closeTime()); wipe(*ledger); ledger->rawInsert(sle(1)); - ReadView& v0 = *ledger; + ReadView const& v0 = *ledger; ApplyViewImpl v1(&v0, tapNONE); { Sandbox v2(&v1); @@ -945,9 +966,9 @@ class View_test : public beast::unit_test::suite // Make sure OpenLedger::empty works { Env env(*this); - BEAST_EXPECT(env.app().openLedger().empty()); + BEAST_EXPECT(env.app().getOpenLedger().empty()); env.fund(XRP(10000), Account("test")); - BEAST_EXPECT(!env.app().openLedger().empty()); + BEAST_EXPECT(!env.app().getOpenLedger().empty()); } } diff --git a/src/test/nodestore/Backend_test.cpp b/src/test/nodestore/Backend_test.cpp index 19f3bc43e5..101138de88 100644 --- a/src/test/nodestore/Backend_test.cpp +++ b/src/test/nodestore/Backend_test.cpp @@ -26,7 +26,7 @@ public: testcase("Backend type=" + type); Section params; - beast::temp_dir tempDir; + beast::temp_dir const tempDir; params.set("type", type); params.set("path", tempDir.path()); diff --git a/src/test/nodestore/Basics_test.cpp b/src/test/nodestore/Basics_test.cpp index e37544f58e..c9755d04d7 100644 --- a/src/test/nodestore/Basics_test.cpp +++ b/src/test/nodestore/Basics_test.cpp @@ -38,7 +38,7 @@ public: for (int i = 0; i < batch.size(); ++i) { - EncodedBlob encoded(batch[i]); + EncodedBlob const encoded(batch[i]); DecodedBlob decoded(encoded.getKey(), encoded.getData(), encoded.getSize()); diff --git a/src/test/nodestore/Database_test.cpp b/src/test/nodestore/Database_test.cpp index 6943f0733e..43bda9dcae 100644 --- a/src/test/nodestore/Database_test.cpp +++ b/src/test/nodestore/Database_test.cpp @@ -191,7 +191,7 @@ public: try { - Env env( + Env const env( *this, std::move(p), std::make_unique(expected, &found), @@ -220,7 +220,7 @@ public: try { - Env env( + Env const env( *this, std::move(p), std::make_unique(expected, &found), @@ -249,7 +249,7 @@ public: try { - Env env( + Env const env( *this, std::move(p), std::make_unique(expected, &found), @@ -278,7 +278,7 @@ public: try { - Env env( + Env const env( *this, std::move(p), std::make_unique(expected, &found), @@ -306,7 +306,7 @@ public: try { - Env env( + Env const env( *this, std::move(p), std::make_unique(expected, &found), @@ -334,7 +334,7 @@ public: try { - Env env( + Env const env( *this, std::move(p), std::make_unique(expected, &found), @@ -362,7 +362,7 @@ public: try { - Env env( + Env const env( *this, std::move(p), std::make_unique(expected, &found), @@ -390,7 +390,7 @@ public: try { - Env env( + Env const env( *this, std::move(p), std::make_unique(expected, &found), @@ -445,7 +445,7 @@ public: } try { - Env env( + Env const env( *this, std::move(p), std::make_unique(expected, &found), @@ -468,7 +468,7 @@ public: } try { - Env env( + Env const env( *this, std::move(p), std::make_unique(expected, &found), @@ -491,7 +491,7 @@ public: } try { - Env env( + Env const env( *this, std::move(p), std::make_unique(expected, &found), @@ -515,7 +515,7 @@ public: { DummyScheduler scheduler; - beast::temp_dir node_db; + beast::temp_dir const node_db; Section srcParams; srcParams.set("type", srcBackendType); srcParams.set("path", node_db.path()); @@ -538,7 +538,7 @@ public: Manager::instance().make_Database(megabytes(4), scheduler, 2, srcParams, journal_); // Set up the destination database - beast::temp_dir dest_db; + beast::temp_dir const dest_db; Section destParams; destParams.set("type", destBackendType); destParams.set("path", dest_db.path()); @@ -572,11 +572,11 @@ public: { DummyScheduler scheduler; - std::string s = "NodeStore backend '" + type + "'"; + std::string const s = "NodeStore backend '" + type + "'"; testcase(s); - beast::temp_dir node_db; + beast::temp_dir const node_db; Section nodeParams; nodeParams.set("type", type); nodeParams.set("path", node_db.path()); @@ -639,7 +639,7 @@ public: try { nodeParams.set("earliest_seq", "0"); - std::unique_ptr db = Manager::instance().make_Database( + std::unique_ptr const db = Manager::instance().make_Database( megabytes(4), scheduler, 2, nodeParams, journal_); } catch (std::runtime_error const& e) @@ -662,7 +662,7 @@ public: { // Set to default earliest ledger sequence nodeParams.set("earliest_seq", std::to_string(XRP_LEDGER_EARLIEST_SEQ)); - std::unique_ptr db2 = Manager::instance().make_Database( + std::unique_ptr const db2 = Manager::instance().make_Database( megabytes(4), scheduler, 2, nodeParams, journal_); } catch (std::runtime_error const& e) diff --git a/src/test/nodestore/NuDBFactory_test.cpp b/src/test/nodestore/NuDBFactory_test.cpp index 9a9b7d83bd..3e6b68c9e5 100644 --- a/src/test/nodestore/NuDBFactory_test.cpp +++ b/src/test/nodestore/NuDBFactory_test.cpp @@ -17,7 +17,7 @@ class NuDBFactory_test : public TestBase { private: // Helper function to create a Section with specified parameters - Section + static Section createSection(std::string const& path, std::string const& blockSize = "") { Section params; @@ -76,12 +76,12 @@ private: std::string const& expectedMessage) { test::StreamSink sink(level); - beast::Journal journal(sink); + beast::Journal const journal(sink); DummyScheduler scheduler; auto backend = Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); - std::string logOutput = sink.messages().str(); + std::string const logOutput = sink.messages().str(); BEAST_EXPECT(logOutput.find(expectedMessage) != std::string::npos); } @@ -89,17 +89,17 @@ private: void testPowerOfTwoValidation(std::string const& size, bool shouldWork) { - beast::temp_dir tempDir; + beast::temp_dir const tempDir; auto params = createSection(tempDir.path(), size); test::StreamSink sink(beast::severities::kWarning); - beast::Journal journal(sink); + beast::Journal const journal(sink); DummyScheduler scheduler; auto backend = Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); - std::string logOutput = sink.messages().str(); - bool hasWarning = logOutput.find("Invalid nudb_block_size") != std::string::npos; + std::string const logOutput = sink.messages().str(); + bool const hasWarning = logOutput.find("Invalid nudb_block_size") != std::string::npos; BEAST_EXPECT(hasWarning == !shouldWork); } @@ -110,7 +110,7 @@ public: { testcase("Default block size (no nudb_block_size specified)"); - beast::temp_dir tempDir; + beast::temp_dir const tempDir; auto params = createSection(tempDir.path()); // Should work with default 4096 block size @@ -122,18 +122,18 @@ public: { testcase("Valid block sizes"); - std::vector validSizes = {4096, 8192, 16384, 32768}; + std::vector const validSizes = {4096, 8192, 16384, 32768}; for (auto const& size : validSizes) { - beast::temp_dir tempDir; + beast::temp_dir const tempDir; auto params = createSection(tempDir.path(), to_string(size)); BEAST_EXPECT(testBackendFunctionality(params, size)); } // Empty value is ignored by the config parser, so uses the // default - beast::temp_dir tempDir; + beast::temp_dir const tempDir; auto params = createSection(tempDir.path(), ""); BEAST_EXPECT(testBackendFunctionality(params, 4096)); @@ -144,7 +144,7 @@ public: { testcase("Invalid block sizes"); - std::vector invalidSizes = { + std::vector const invalidSizes = { "2048", // Too small "1024", // Too small "65536", // Too large @@ -161,7 +161,7 @@ public: for (auto const& size : invalidSizes) { - beast::temp_dir tempDir; + beast::temp_dir const tempDir; auto params = createSection(tempDir.path(), size); // Fails @@ -169,14 +169,14 @@ public: } // Test whitespace cases separately since lexical_cast may handle them - std::vector whitespaceInvalidSizes = { + std::vector const whitespaceInvalidSizes = { "4096 ", // Trailing space - might be handled by lexical_cast " 4096" // Leading space - might be handled by lexical_cast }; for (auto const& size : whitespaceInvalidSizes) { - beast::temp_dir tempDir; + beast::temp_dir const tempDir; auto params = createSection(tempDir.path(), size); // Fails @@ -191,7 +191,7 @@ public: // Test valid custom block size logging { - beast::temp_dir tempDir; + beast::temp_dir const tempDir; auto params = createSection(tempDir.path(), "8192"); testLogMessage(params, beast::severities::kInfo, "Using custom NuDB block size: 8192"); @@ -199,11 +199,11 @@ public: // Test invalid block size failure { - beast::temp_dir tempDir; + beast::temp_dir const tempDir; auto params = createSection(tempDir.path(), "5000"); test::StreamSink sink(beast::severities::kWarning); - beast::Journal journal(sink); + beast::Journal const journal(sink); DummyScheduler scheduler; try @@ -214,7 +214,7 @@ public: } catch (std::exception const& e) { - std::string logOutput{e.what()}; + 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") != @@ -224,11 +224,11 @@ public: // Test non-numeric value failure { - beast::temp_dir tempDir; + beast::temp_dir const tempDir; auto params = createSection(tempDir.path(), "invalid"); test::StreamSink sink(beast::severities::kWarning); - beast::Journal journal(sink); + beast::Journal const journal(sink); DummyScheduler scheduler; try @@ -240,7 +240,7 @@ public: } catch (std::exception const& e) { - std::string logOutput{e.what()}; + std::string const logOutput{e.what()}; BEAST_EXPECT( logOutput.find("Invalid nudb_block_size value: invalid") != std::string::npos); } @@ -253,7 +253,7 @@ public: testcase("Power of 2 validation logic"); // Test edge cases around valid range - std::vector> testCases = { + std::vector> const testCases = { {"4095", false}, // Just below minimum {"4096", true}, // Minimum valid {"4097", false}, // Just above minimum, not power of 2 @@ -267,13 +267,13 @@ public: for (auto const& [size, shouldWork] : testCases) { - beast::temp_dir tempDir; + beast::temp_dir const tempDir; auto params = createSection(tempDir.path(), size); // We test the validation logic by catching exceptions for invalid // values test::StreamSink sink(beast::severities::kWarning); - beast::Journal journal(sink); + beast::Journal const journal(sink); DummyScheduler scheduler; try @@ -284,7 +284,7 @@ public: } catch (std::exception const& e) { - std::string logOutput{e.what()}; + std::string const logOutput{e.what()}; BEAST_EXPECT(logOutput.find("Invalid nudb_block_size") != std::string::npos); } } @@ -295,7 +295,7 @@ public: { testcase("Both constructor variants work with custom block size"); - beast::temp_dir tempDir; + beast::temp_dir const tempDir; auto params = createSection(tempDir.path(), "16384"); DummyScheduler scheduler; @@ -321,13 +321,13 @@ public: testcase("Configuration parsing edge cases"); // Test that whitespace is handled correctly - std::vector validFormats = { + std::vector const validFormats = { "8192" // Basic valid format }; // Test whitespace handling separately since lexical_cast behavior may // vary - std::vector whitespaceFormats = { + std::vector const whitespaceFormats = { " 8192", // Leading space - may or may not be handled by // lexical_cast "8192 " // Trailing space - may or may not be handled by @@ -337,19 +337,19 @@ public: // Test basic valid format for (auto const& format : validFormats) { - beast::temp_dir tempDir; + beast::temp_dir const tempDir; auto params = createSection(tempDir.path(), format); test::StreamSink sink(beast::severities::kInfo); - beast::Journal journal(sink); + beast::Journal const journal(sink); DummyScheduler scheduler; auto backend = Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); // Should log success message for valid values - std::string logOutput = sink.messages().str(); - bool hasSuccessMessage = + std::string const logOutput = sink.messages().str(); + bool const hasSuccessMessage = logOutput.find("Using custom NuDB block size") != std::string::npos; BEAST_EXPECT(hasSuccessMessage); } @@ -358,12 +358,12 @@ public: // them for (auto const& format : whitespaceFormats) { - beast::temp_dir tempDir; + beast::temp_dir const tempDir; auto params = createSection(tempDir.path(), format); // Use a lower threshold to capture both info and warning messages test::StreamSink sink(beast::severities::kDebug); - beast::Journal journal(sink); + beast::Journal const journal(sink); DummyScheduler scheduler; try @@ -385,11 +385,11 @@ public: { testcase("Data persistence with different block sizes"); - std::vector blockSizes = {"4096", "8192", "16384", "32768"}; + std::vector const blockSizes = {"4096", "8192", "16384", "32768"}; for (auto const& size : blockSizes) { - beast::temp_dir tempDir; + beast::temp_dir const tempDir; auto params = createSection(tempDir.path(), size); DummyScheduler scheduler; diff --git a/src/test/nodestore/TestBase.h b/src/test/nodestore/TestBase.h index cb2a8e3bd5..893e06579c 100644 --- a/src/test/nodestore/TestBase.h +++ b/src/test/nodestore/TestBase.h @@ -187,7 +187,7 @@ public: for (int i = 0; i < batch.size(); ++i) { - std::shared_ptr object = db.fetchNodeObject(batch[i]->getHash(), 0); + std::shared_ptr const object = db.fetchNodeObject(batch[i]->getHash(), 0); if (object != nullptr) pCopy->push_back(object); diff --git a/src/test/nodestore/Timing_test.cpp b/src/test/nodestore/Timing_test.cpp index b537e3abb7..39e8b59638 100644 --- a/src/test/nodestore/Timing_test.cpp +++ b/src/test/nodestore/Timing_test.cpp @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -110,7 +111,7 @@ public: { b.clear(); b.reserve(size); - while (size--) + while ((size--) != 0u) b.emplace_back(obj(n++)); } }; @@ -489,7 +490,7 @@ public: backend->close(); } - // Simulate a rippled workload: + // Simulate an xrpld workload: // Each thread randomly: // inserts a new key // fetches an old key @@ -523,7 +524,7 @@ public: , seq1_(1) , gen_(id + 1) , rand_(0, 99) - , recent_(params.items, params.items * 2 - 1) + , recent_(params.items, (params.items * 2) - 1) , older_(0, params.items - 1) { } @@ -550,6 +551,7 @@ public: p[1] = 1 - p[0]; for (int q = 0; q < 2; ++q) { + // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (p[q]) { case 0: { @@ -620,8 +622,9 @@ public: using std::setw; int w = 8; for (auto const& test : tests) - if (w < test.first.size()) - w = test.first.size(); + { + w = std::max::size_type>(w, test.first.size()); + } log << threads << " Thread" << (threads > 1 ? "s" : "") << ", " << default_items << " Objects" << std::endl; { @@ -637,19 +640,21 @@ public: for (auto const& config_string : config_strings) { - Params params; + Params params{}; params.items = default_items; params.threads = threads; - for (auto i = default_repeat; i--;) + for (auto i = default_repeat; (i--) != 0u;) { - beast::temp_dir tempDir; + beast::temp_dir const tempDir; Section config = parse(config_string); config.set("path", tempDir.path()); std::stringstream ss; ss << std::left << setw(10) << get(config, "type", std::string()) << std::right; for (auto const& test : tests) + { ss << " " << setw(w) << to_string(do_test(test.second, config, params, journal)); + } ss << " " << to_string(config); log << ss.str() << std::endl; } @@ -667,7 +672,7 @@ public: items Number of objects to create in the database */ - std::string default_args = + std::string const default_args = "type=nudb" #if XRPL_ROCKSDB_AVAILABLE ";type=rocksdb,open_files=2000,filter_bits=12,cache_mb=256," @@ -689,10 +694,16 @@ public: std::vector config_strings; boost::split(config_strings, args, boost::algorithm::is_any_of(";")); for (auto iter = config_strings.begin(); iter != config_strings.end();) + { if (iter->empty()) + { iter = config_strings.erase(iter); + } else + { ++iter; + } + } do_tests(1, tests, config_strings); do_tests(4, tests, config_strings); diff --git a/src/test/nodestore/import_test.cpp b/src/test/nodestore/import_test.cpp index 578dd3670e..6d336c1f51 100644 --- a/src/test/nodestore/import_test.cpp +++ b/src/test/nodestore/import_test.cpp @@ -71,7 +71,7 @@ template std::ostream& pretty_time(std::ostream& os, std::chrono::duration d) { - save_stream_state _(os); + save_stream_state const _(os); using namespace std::chrono; if (d < microseconds{1}) { @@ -332,14 +332,14 @@ public: options.create_if_missing = false; options.max_open_files = 2000; // 5000? rocksdb::DB* pdb = nullptr; - rocksdb::Status status = rocksdb::DB::OpenForReadOnly(options, from_path, &pdb); - if (!status.ok() || !pdb) + rocksdb::Status const status = rocksdb::DB::OpenForReadOnly(options, from_path, &pdb); + if (!status.ok() || (pdb == nullptr)) Throw("Can't open '" + from_path + "': " + status.ToString()); db.reset(pdb); } // Create data file with values std::size_t nitems = 0; - dat_file_header dh; + dat_file_header dh{}; dh.version = currentVersion; dh.uid = make_uid(); dh.appnum = 1; @@ -367,12 +367,14 @@ public: for (it->SeekToFirst(); it->Valid(); it->Next()) { if (it->key().size() != 32) + { Throw( "Unexpected key size " + std::to_string(it->key().size())); + } void const* const key = it->key().data(); void const* const data = it->value().data(); auto const size = it->value().size(); - std::unique_ptr clean(new char[size]); + std::unique_ptr const clean(new char[size]); std::memcpy(clean.get(), data, size); filter_inner(clean.get(), size); auto const out = nodeobject_compress(clean.get(), size, buf); @@ -406,7 +408,7 @@ public: if (ec) Throw(ec); // Create key file - key_file_header kh; + key_file_header kh{}; kh.version = currentVersion; kh.uid = dh.uid; kh.appnum = dh.appnum; @@ -456,7 +458,7 @@ public: // Create empty buckets for (std::size_t i = 0; i < bn; ++i) { - bucket b(kh.block_size, buf.get() + i * kh.block_size, empty); + bucket const b(kh.block_size, buf.get() + (i * kh.block_size), empty); } // Insert all keys into buckets // Iterate Data File @@ -465,7 +467,7 @@ public: { auto const offset = r.offset(); // Data Record or Spill Record - std::size_t size; + std::size_t size = 0; auto is = r.prepare(field::size, ec); // Size if (ec) Throw(ec); @@ -482,10 +484,10 @@ public: std::uint8_t const* const key = is.data(dh.key_size); auto const h = hash(key, kh.key_size, kh.salt); auto const n = bucket_index(h, kh.buckets, kh.modulus); - p(log, npass * df_size + r.offset()); + p(log, (npass * df_size) + r.offset()); if (n < b0 || n >= b1) continue; - bucket b(kh.block_size, buf.get() + (n - b0) * kh.block_size); + bucket b(kh.block_size, buf.get() + ((n - b0) * kh.block_size)); maybe_spill(b, dw, ec); if (ec) Throw(ec); diff --git a/src/test/nodestore/varint_test.cpp b/src/test/nodestore/varint_test.cpp index 7ef624e777..f6145a9f52 100644 --- a/src/test/nodestore/varint_test.cpp +++ b/src/test/nodestore/varint_test.cpp @@ -17,11 +17,11 @@ public: testcase("encode, decode"); for (auto const v : vv) { - std::array::max> vi; + std::array::max> vi{}; auto const n0 = write_varint(vi.data(), v); expect(n0 > 0, "write error"); expect(n0 == size_varint(v), "size error"); - std::size_t v1; + std::size_t v1 = 0; auto const n1 = read_varint(vi.data(), n0, v1); expect(n1 == n0, "read error"); expect(v == v1, "wrong value"); diff --git a/src/test/overlay/ProtocolVersion_test.cpp b/src/test/overlay/ProtocolVersion_test.cpp index f0d02edbca..fc25812cbb 100644 --- a/src/test/overlay/ProtocolVersion_test.cpp +++ b/src/test/overlay/ProtocolVersion_test.cpp @@ -42,20 +42,14 @@ public: // Empty string check("", ""); - // clang-format off - check( - "RTXP/1.1,RTXP/1.2,RTXP/1.3,XRPL/2.1,XRPL/2.0,/XRPL/3.0", - "XRPL/2.0,XRPL/2.1"); - check( - "RTXP/0.9,RTXP/1.01,XRPL/0.3,XRPL/2.01,websocket", - ""); + check("RTXP/1.1,RTXP/1.2,RTXP/1.3,XRPL/2.1,XRPL/2.0,/XRPL/3.0", "XRPL/2.0,XRPL/2.1"); + check("RTXP/0.9,RTXP/1.01,XRPL/0.3,XRPL/2.01,websocket", ""); check( "XRPL/2.0,XRPL/2.0,XRPL/19.4,XRPL/7.89,XRPL/XRPL/3.0,XRPL/2.01", "XRPL/2.0,XRPL/7.89,XRPL/19.4"); check( "XRPL/2.0,XRPL/3.0,XRPL/4,XRPL/,XRPL,OPT XRPL/2.2,XRPL/5.67", "XRPL/2.0,XRPL/3.0,XRPL/5.67"); - // clang-format on } { diff --git a/src/test/overlay/TMGetObjectByHash_test.cpp b/src/test/overlay/TMGetObjectByHash_test.cpp index 0624c7b088..a2a934f182 100644 --- a/src/test/overlay/TMGetObjectByHash_test.cpp +++ b/src/test/overlay/TMGetObjectByHash_test.cpp @@ -95,15 +95,15 @@ class TMGetObjectByHash_test : public beast::unit_test::suite std::shared_ptr createPeer(jtx::Env& env) { - auto& overlay = dynamic_cast(env.app().overlay()); + auto& overlay = dynamic_cast(env.app().getOverlay()); boost::beast::http::request request; auto stream_ptr = std::make_unique(socket_type(env.app().getIOContext()), *context_); - beast::IP::Endpoint local(boost::asio::ip::make_address("172.1.1.1"), 51235); - beast::IP::Endpoint remote(boost::asio::ip::make_address("172.1.1.2"), 51235); + beast::IP::Endpoint const local(boost::asio::ip::make_address("172.1.1.1"), 51235); + beast::IP::Endpoint const remote(boost::asio::ip::make_address("172.1.1.2"), 51235); - PublicKey key(std::get<0>(randomKeyPair(KeyType::ed25519))); + PublicKey const key(std::get<0>(randomKeyPair(KeyType::ed25519))); auto consumer = overlay.resourceManager().newInboundEndpoint(remote); auto [slot, _] = overlay.peerFinder().new_inbound_slot(local, remote); @@ -121,7 +121,7 @@ class TMGetObjectByHash_test : public beast::unit_test::suite return peer; } - std::shared_ptr + static std::shared_ptr createRequest(size_t const numObjects, Env& env) { // Store objects in the NodeStore that will be found during the query @@ -132,7 +132,7 @@ class TMGetObjectByHash_test : public beast::unit_test::suite hashes.reserve(numObjects); for (int i = 0; i < numObjects; ++i) { - uint256 hash(xrpl::sha512Half(i)); + uint256 const hash(xrpl::sha512Half(i)); hashes.push_back(hash); Blob data(100, static_cast(i % 256)); diff --git a/src/test/overlay/cluster_test.cpp b/src/test/overlay/cluster_test.cpp index 60fd5d62b3..6fc7f3f59b 100644 --- a/src/test/overlay/cluster_test.cpp +++ b/src/test/overlay/cluster_test.cpp @@ -29,7 +29,7 @@ public: return cluster; } - PublicKey + static PublicKey randomNode() { return derivePublicKey(KeyType::secp256k1, randomSecretKey()); @@ -113,7 +113,7 @@ public: auto const node = randomNode(); auto const name = toBase58(TokenType::NodePublic, node); - std::uint32_t load = 0; + std::uint32_t const load = 0; NetClock::time_point tick = {}; // Initial update @@ -121,7 +121,7 @@ public: { auto member = c->member(node); BEAST_EXPECT(static_cast(member)); - BEAST_EXPECT(member->empty()); + BEAST_EXPECT(member->empty()); // NOLINT(bugprone-unchecked-optional-access) } // Updating too quickly: should fail @@ -129,7 +129,7 @@ public: { auto member = c->member(node); BEAST_EXPECT(static_cast(member)); - BEAST_EXPECT(member->empty()); + BEAST_EXPECT(member->empty()); // NOLINT(bugprone-unchecked-optional-access) } using namespace std::chrono_literals; @@ -140,7 +140,7 @@ public: { auto member = c->member(node); BEAST_EXPECT(static_cast(member)); - BEAST_EXPECT(member->compare(name) == 0); + BEAST_EXPECT(member->compare(name) == 0); // NOLINT(bugprone-unchecked-optional-access) } // Updating the name (non-empty doesn't go to empty) @@ -149,7 +149,7 @@ public: { auto member = c->member(node); BEAST_EXPECT(static_cast(member)); - BEAST_EXPECT(member->compare(name) == 0); + BEAST_EXPECT(member->compare(name) == 0); // NOLINT(bugprone-unchecked-optional-access) } // Updating the name (non-empty updates to new non-empty) @@ -158,7 +158,8 @@ public: { auto member = c->member(node); BEAST_EXPECT(static_cast(member)); - BEAST_EXPECT(member->compare("test") == 0); + BEAST_EXPECT( + member->compare("test") == 0); // NOLINT(bugprone-unchecked-optional-access) } } diff --git a/src/test/overlay/compression_test.cpp b/src/test/overlay/compression_test.cpp index 3c787a4c72..4ffc805726 100644 --- a/src/test/overlay/compression_test.cpp +++ b/src/test/overlay/compression_test.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -15,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -117,7 +117,7 @@ public: decompressed.begin())); } - std::shared_ptr + static std::shared_ptr buildManifests(int n) { auto manifests = std::make_shared(); @@ -143,7 +143,7 @@ public: return manifests; } - std::shared_ptr + static std::shared_ptr buildEndpoints(int n) { auto endpoints = std::make_shared(); @@ -163,7 +163,7 @@ public: buildTransaction(Logs& logs) { Env env(*this, envconfig()); - int fund = 10000; + int const fund = 10000; auto const alice = Account("alice"); auto const bob = Account("bob"); env.fund(XRP(fund), "alice", "bob"); @@ -176,7 +176,7 @@ public: return std::string{reinterpret_cast(blob->data()), blob->size()}; }; - std::string usdTxBlob = ""; + std::string usdTxBlob; auto wsc = makeWSClient(env.app().config()); { Json::Value requestUSD; @@ -196,7 +196,7 @@ public: return transaction; } - std::shared_ptr + static std::shared_ptr buildGetLedger() { auto getLedger = std::make_shared(); @@ -205,7 +205,7 @@ public: uint256 const hash(xrpl::sha512Half(123456789)); getLedger->set_ledgerhash(hash.begin(), hash.size()); getLedger->set_ledgerseq(123456789); - xrpl::SHAMapNodeID sha(64, hash); + xrpl::SHAMapNodeID const sha(64, hash); getLedger->add_nodeids(sha.getRawString()); getLedger->set_requestcookie(123456789); getLedger->set_querytype(protocol::qtINDIRECT); @@ -213,7 +213,7 @@ public: return getLedger; } - std::shared_ptr + static std::shared_ptr buildLedgerData(uint32_t n, Logs& logs) { auto ledgerData = std::make_shared(); @@ -251,7 +251,7 @@ public: return ledgerData; } - std::shared_ptr + static std::shared_ptr buildGetObjectByHash() { auto getObject = std::make_shared(); @@ -268,7 +268,7 @@ public: uint256 hash(xrpl::sha512Half(i)); auto object = getObject->add_objects(); object->set_hash(hash.data(), hash.size()); - xrpl::SHAMapNodeID sha(64, hash); + xrpl::SHAMapNodeID const sha(64, hash); object->set_nodeid(sha.getRawString()); object->set_index(""); object->set_data(""); @@ -277,7 +277,7 @@ public: return getObject; } - std::shared_ptr + static std::shared_ptr buildValidatorList() { auto list = std::make_shared(); @@ -295,7 +295,7 @@ public: st.add(s); list->set_manifest(s.data(), s.size()); list->set_version(3); - STObject signature(sfSignature); + STObject const signature(sfSignature); xrpl::sign(st, HashPrefix::manifest, KeyType::ed25519, std::get<1>(signing)); Serializer s1; st.add(s1); @@ -304,7 +304,7 @@ public: return list; } - std::shared_ptr + static std::shared_ptr buildValidatorListCollection() { auto list = std::make_shared(); @@ -322,7 +322,7 @@ public: st.add(s); list->set_manifest(s.data(), s.size()); list->set_version(4); - STObject signature(sfSignature); + STObject const signature(sfSignature); xrpl::sign(st, HashPrefix::manifest, KeyType::ed25519, std::get<1>(signing)); Serializer s1; st.add(s1); @@ -338,14 +338,14 @@ public: auto thresh = beast::severities::Severity::kInfo; auto logs = std::make_unique(thresh); - protocol::TMManifests manifests; - protocol::TMEndpoints endpoints; - protocol::TMTransaction transaction; - protocol::TMGetLedger get_ledger; - protocol::TMLedgerData ledger_data; - protocol::TMGetObjectByHash get_object; - protocol::TMValidatorList validator_list; - protocol::TMValidatorListCollection validator_list_collection; + protocol::TMManifests const manifests; + protocol::TMEndpoints const endpoints; + protocol::TMTransaction const transaction; + protocol::TMGetLedger const get_ledger; + protocol::TMLedgerData const ledger_data; + protocol::TMGetObjectByHash const get_object; + protocol::TMValidatorList const validator_list; + protocol::TMValidatorListCollection const validator_list_collection; // 4.5KB doTest(buildManifests(20), protocol::mtMANIFESTS, 4, "TMManifests20"); @@ -399,7 +399,7 @@ public: return env; }; auto handshake = [&](int outboundEnable, int inboundEnable) { - beast::IP::Address addr = boost::asio::ip::make_address("172.1.1.100"); + beast::IP::Address const addr = boost::asio::ip::make_address("172.1.1.100"); auto env = getEnv(outboundEnable); auto request = xrpl::makeRequest( diff --git a/src/test/overlay/reduce_relay_test.cpp b/src/test/overlay/reduce_relay_test.cpp index bf6d8aac1c..bac70d35a6 100644 --- a/src/test/overlay/reduce_relay_test.cpp +++ b/src/test/overlay/reduce_relay_test.cpp @@ -14,6 +14,7 @@ #include +#include #include #include #include @@ -119,7 +120,7 @@ public: uint256 const& getClosedLedgerHash() const override { - static uint256 hash{}; + static uint256 const hash{}; return hash; } bool @@ -249,7 +250,7 @@ public: Validator& validator, PeerSPtr peer, Latency const& latency = {milliseconds(5), milliseconds(15)}) - : validator_(validator), peer_(peer), latency_(latency), up_(true) + : validator_(validator), peer_(peer), latency_(latency) { auto sp = peer_.lock(); assert(sp); @@ -294,7 +295,7 @@ private: Validator& validator_; PeerWPtr peer_; Latency latency_; - bool up_; + bool up_{true}; }; /** Simulate Validator */ @@ -361,7 +362,7 @@ public: { for (auto id : peers) { - assert(links_.find(id) != links_.end()); + assert(links_.contains(id)); f(*links_[id], message_); } } @@ -404,7 +405,7 @@ public: } std::uint16_t - id() + id() const { return id_; } @@ -468,10 +469,11 @@ public: { auto validator = m->getValidatorKey(); assert(validator); - if (!squelch_.expireSquelch(*validator)) + if (!squelch_.expireSquelch(*validator)) // NOLINT(bugprone-unchecked-optional-access) return; - overlay_.updateSlotAndSquelch({}, *validator, id(), f); + overlay_.updateSlotAndSquelch( + {}, *validator, id(), f); // NOLINT(bugprone-unchecked-optional-access) } /** Remote Peer (Directly connected Peer) */ @@ -479,11 +481,15 @@ public: onMessage(protocol::TMSquelch const& squelch) override { auto validator = squelch.validatorpubkey(); - PublicKey key(Slice(validator.data(), validator.size())); + PublicKey const key(Slice(validator.data(), validator.size())); if (squelch.squelch()) + { squelch_.addSquelch(key, std::chrono::seconds{squelch.squelchduration()}); + } else + { squelch_.removeSquelch(key); + } } private: @@ -501,7 +507,7 @@ class OverlaySim : public Overlay, public reduce_relay::SquelchHandler public: using id_t = Peer::id_t; using clock_type = ManualClock; - OverlaySim(Application& app) : slots_(app.logs(), *this, app.config()), logs_(app.logs()) + OverlaySim(Application& app) : slots_(app, *this, app.config()), registry_(app) { } @@ -552,10 +558,10 @@ public: addPeer(bool useCache = true) { PeerSPtr peer{}; - Peer::id_t id; + Peer::id_t id = 0; if (peersCache_.empty() || !useCache) { - peer = std::make_shared(*this, logs_.journal("Squelch")); + peer = std::make_shared(*this, registry_.getJournal("Squelch")); id = peer->id(); } else @@ -600,8 +606,7 @@ public: for (auto& [id, _] : peers_) { (void)_; - if (id > maxId) - maxId = id; + maxId = std::max(id, maxId); } deletePeer(maxId, false); @@ -625,14 +630,14 @@ public: isSelected(PublicKey const& validator, Peer::id_t peer) { auto selected = slots_.getSelected(validator); - return selected.find(peer) != selected.end(); + return selected.contains(peer); } id_t getSelectedPeer(PublicKey const& validator) { auto selected = slots_.getSelected(validator); - assert(selected.size()); + assert(!selected.empty()); return *selected.begin(); } @@ -668,7 +673,7 @@ private: Peers peers_; Peers peersCache_; reduce_relay::Slots slots_; - Logs& logs_; + ServiceRegistry& registry_; }; class Network @@ -751,9 +756,13 @@ public: validators_.begin(), validators_.end(), [&](auto& v) { return v.id() == validatorId; }); assert(it != validators_.end()); if (enable) + { it->linkUp(peer); + } else + { it->linkDown(peer); + } } void @@ -767,7 +776,7 @@ public: squelch.set_squelch(false); for (auto& v : validators_) { - PublicKey key = v; + PublicKey const key = v; squelch.clear_validatorpubkey(); squelch.set_validatorpubkey(key.data(), key.size()); v.for_links({peer}, [&](Link& l, MessageSPtr) { @@ -776,7 +785,7 @@ public: } } - void + static void for_rand(std::uint32_t min, std::uint32_t max, std::function f) { auto size = max - min; @@ -870,14 +879,14 @@ protected: } /** Send squelch (if duration is set) or unsquelch (if duration not set) */ - Peer::id_t + static Peer::id_t sendSquelch( PublicKey const& validator, PeerWPtr const& peerPtr, std::optional duration) { protocol::TMSquelch squelch; - bool res = duration ? true : false; + bool const res = static_cast(duration); squelch.set_squelch(res); squelch.set_validatorpubkey(validator.data(), validator.size()); if (res) @@ -900,8 +909,8 @@ protected: std::uint32_t cnt_ = 0; std::uint32_t handledCnt_ = 0; bool isSelected_ = false; - Peer::id_t peer_; - std::uint16_t validator_; + Peer::id_t peer_{}; + std::uint16_t validator_{}; std::optional key_; time_point time_; bool handled_ = false; @@ -939,9 +948,11 @@ protected: for (auto s : selected) str << s << " "; if (log) + { std::cout << (double)reduce_relay::epoch(now).count() / 1000. << " random, squelched, validator: " << validator.id() << " peers: " << str.str() << std::endl; + } auto countingState = network_.overlay().isCountingState(validator); BEAST_EXPECT( countingState == false && @@ -967,7 +978,9 @@ protected: network_.overlay().isSelected(validator, link.peerId()); } else + { events[event].isSelected_ = network_.isSelected(link.peerId()); + } }; auto r = rand_int(0, 1000); if (r == (int)EventType::LinkDown || r == (int)EventType::PeerDisconnected) @@ -979,7 +992,7 @@ protected: if (events[EventType::PeerDisconnected].state_ == State::On) { auto& event = events[EventType::PeerDisconnected]; - bool allCounting = network_.allCounting(event.peer_); + bool const allCounting = network_.allCounting(event.peer_); network_.overlay().deletePeer( event.peer_, [&](PublicKey const& v, PeerWPtr const& peerPtr) { if (event.isSelected_) @@ -991,8 +1004,8 @@ protected: // take place because there is no peers in Squelched state in // any of the slots where the peer is in Selected state // (allCounting is true) - bool handled = (event.isSelected_ == false && !event.handled_) || - (event.isSelected_ == true && (event.handled_ || allCounting)); + bool const handled = (!event.isSelected_ && !event.handled_) || + (event.isSelected_ && (event.handled_ || allCounting)); BEAST_EXPECT(handled); event.state_ = State::Off; event.isSelected_ = false; @@ -1024,7 +1037,7 @@ protected: d > milliseconds(reduce_relay::IDLED).count() && network_.overlay().inState( *event.key_, reduce_relay::PeerState::Squelched) > 0 && - peers.find(event.peer_) != peers.end(); + peers.contains(event.peer_); } network_.overlay().deleteIdlePeers([&](PublicKey const& v, PeerWPtr const& ptr) { event.handled_ = true; @@ -1034,7 +1047,7 @@ protected: sendSquelch(validator, ptr, {}); } }); - bool handled = (event.handled_ && event.state_ == State::WaitReset) || + bool const handled = (event.handled_ && event.state_ == State::WaitReset) || (!event.handled_ && !mustHandle); BEAST_EXPECT(handled); } @@ -1042,7 +1055,7 @@ protected: (event.state_ == State::On && (now - event.time_ > (reduce_relay::IDLED + seconds(2))))) { - bool handled = event.state_ == State::WaitReset || !event.handled_; + bool const handled = event.state_ == State::WaitReset || !event.handled_; BEAST_EXPECT(handled); event.state_ = State::Off; event.isSelected_ = false; @@ -1059,9 +1072,11 @@ protected: // All Peer Disconnect events must be handled BEAST_EXPECT(disconnected.cnt_ == disconnected.handledCnt_); if (log) + { std::cout << "link down count: " << down.cnt_ << "/" << down.handledCnt_ << " peer disconnect count: " << disconnected.cnt_ << "/" << disconnected.handledCnt_; + } } bool @@ -1255,10 +1270,10 @@ protected: doTest("Test Config - squelch enabled (legacy)", log, [&](bool log) { Config c; - std::string toLoad(R"rippleConfig( + std::string const toLoad(R"xrpldConfig( [reduce_relay] vp_enable=1 -)rippleConfig"); +)xrpldConfig"); c.loadFromString(toLoad); BEAST_EXPECT(c.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE == true); @@ -1267,19 +1282,19 @@ vp_enable=1 doTest("Test Config - squelch disabled (legacy)", log, [&](bool log) { Config c; - std::string toLoad(R"rippleConfig( + std::string toLoad(R"xrpldConfig( [reduce_relay] vp_enable=0 -)rippleConfig"); +)xrpldConfig"); c.loadFromString(toLoad); BEAST_EXPECT(c.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE == false); Config c1; - toLoad = R"rippleConfig( + toLoad = R"xrpldConfig( [reduce_relay] -)rippleConfig"; +)xrpldConfig"; c1.loadFromString(toLoad); BEAST_EXPECT(c1.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE == false); @@ -1288,10 +1303,10 @@ vp_enable=0 doTest("Test Config - squelch enabled", log, [&](bool log) { Config c; - std::string toLoad(R"rippleConfig( + std::string const toLoad(R"xrpldConfig( [reduce_relay] vp_base_squelch_enable=1 -)rippleConfig"); +)xrpldConfig"); c.loadFromString(toLoad); BEAST_EXPECT(c.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE == true); @@ -1300,10 +1315,10 @@ vp_base_squelch_enable=1 doTest("Test Config - squelch disabled", log, [&](bool log) { Config c; - std::string toLoad(R"rippleConfig( + std::string const toLoad(R"xrpldConfig( [reduce_relay] vp_base_squelch_enable=0 -)rippleConfig"); +)xrpldConfig"); c.loadFromString(toLoad); BEAST_EXPECT(c.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE == false); @@ -1312,11 +1327,11 @@ vp_base_squelch_enable=0 doTest("Test Config - legacy and new", log, [&](bool log) { Config c; - std::string toLoad(R"rippleConfig( + std::string const toLoad(R"xrpldConfig( [reduce_relay] vp_base_squelch_enable=0 vp_enable=0 -)rippleConfig"); +)xrpldConfig"); std::string error; auto const expectedError = @@ -1341,29 +1356,29 @@ vp_enable=0 doTest("Test Config - max selected peers", log, [&](bool log) { Config c; - std::string toLoad(R"rippleConfig( + std::string toLoad(R"xrpldConfig( [reduce_relay] -)rippleConfig"); +)xrpldConfig"); c.loadFromString(toLoad); BEAST_EXPECT(c.VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS == 5); Config c1; - toLoad = R"rippleConfig( + toLoad = R"xrpldConfig( [reduce_relay] vp_base_squelch_max_selected_peers=6 -)rippleConfig"; +)xrpldConfig"; c1.loadFromString(toLoad); BEAST_EXPECT(c1.VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS == 6); Config c2; - toLoad = R"rippleConfig( + toLoad = R"xrpldConfig( [reduce_relay] vp_base_squelch_max_selected_peers=2 -)rippleConfig"; +)xrpldConfig"; std::string error; auto const expectedError = @@ -1391,7 +1406,7 @@ vp_base_squelch_max_selected_peers=2 auto createSlots = [&](bool baseSquelchEnabled) -> reduce_relay::Slots { env_.app().config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = baseSquelchEnabled; return reduce_relay::Slots( - env_.app().logs(), network_.overlay(), env_.app().config()); + env_.app(), network_.overlay(), env_.app().config()); }; // base squelching must not be ready if squelching is disabled BEAST_EXPECT(!createSlots(false).baseSquelchReady()); @@ -1417,10 +1432,10 @@ vp_base_squelch_max_selected_peers=2 doTest("Duplicate Message", log, [&](bool log) { network_.reset(); // update message count for the same peer/validator - std::int16_t nMessages = 5; + std::int16_t const nMessages = 5; for (int i = 0; i < nMessages; i++) { - uint256 key(i); + uint256 const key(i); network_.overlay().updateSlotAndSquelch( key, network_.validator(0), 0, [&](PublicKey const&, PeerWPtr, std::uint32_t) { }); @@ -1430,7 +1445,7 @@ vp_base_squelch_max_selected_peers=2 // hence '-1'. BEAST_EXPECT(std::get<1>(peers[0]) == (nMessages - 1)); // add duplicate - uint256 key(nMessages - 1); + uint256 const key(nMessages - 1); network_.overlay().updateSlotAndSquelch( key, network_.validator(0), 0, [&](PublicKey const&, PeerWPtr, std::uint32_t) {}); // confirm the same number of messages @@ -1448,20 +1463,19 @@ vp_base_squelch_max_selected_peers=2 struct Handler : public reduce_relay::SquelchHandler { - Handler() : maxDuration_(0) + Handler() { } void squelch(PublicKey const&, Peer::id_t, std::uint32_t duration) const override { - if (duration > maxDuration_) - maxDuration_ = duration; + maxDuration_ = std::max(duration, maxDuration_); } void unsquelch(PublicKey const&, Peer::id_t) const override { } - mutable int maxDuration_; + mutable int maxDuration_{0}; }; void @@ -1473,8 +1487,7 @@ vp_base_squelch_max_selected_peers=2 auto run = [&](int npeers) { handler.maxDuration_ = 0; - reduce_relay::Slots slots( - env_.app().logs(), handler, env_.app().config()); + reduce_relay::Slots slots(env_.app(), handler, env_.app().config()); // 1st message from a new peer switches the slot // to counting state and resets the counts of all peers + // MAX_MESSAGE_THRESHOLD + 1 messages to reach the threshold @@ -1485,7 +1498,7 @@ vp_base_squelch_max_selected_peers=2 { // make unique message hash to make the // slot's internal hash router accept the message - std::uint64_t mid = m * 1000 + peer; + std::uint64_t const mid = (m * 1000) + peer; uint256 const message{mid}; slots.updateSlotAndSquelch( message, validator, peer, protocol::MessageType::mtVALIDATION); @@ -1518,18 +1531,22 @@ vp_base_squelch_max_selected_peers=2 handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_PEERS.count()); using namespace beast::unit_test::detail; if (handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_DEFAULT.count()) + { log << make_reason("warning: squelch duration is low", __FILE__, __LINE__) << std::endl << std::flush; + } // more than 400 is still less than MAX_UNSQUELCH_EXPIRE_PEERS run(400); BEAST_EXPECT( handler.maxDuration_ >= MIN_UNSQUELCH_EXPIRE.count() && handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_PEERS.count()); if (handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_DEFAULT.count()) + { log << make_reason("warning: squelch duration is low", __FILE__, __LINE__) << std::endl << std::flush; + } }); } @@ -1551,7 +1568,7 @@ vp_base_squelch_max_selected_peers=2 env_.app().config().COMPRESSION = c.COMPRESSION; }; auto handshake = [&](int outboundEnable, int inboundEnable) { - beast::IP::Address addr = boost::asio::ip::make_address("172.1.1.100"); + beast::IP::Address const addr = boost::asio::ip::make_address("172.1.1.100"); setEnv(outboundEnable); auto request = xrpl::makeRequest( @@ -1605,7 +1622,7 @@ public: void run() override { - bool log = false; + bool const log = false; testConfig(log); testInitialRound(log); testPeerUnsquelchedTooSoon(log); @@ -1632,7 +1649,7 @@ class reduce_relay_simulate_test : public reduce_relay_test void run() override { - bool log = false; + bool const log = false; testRandom(log); } }; diff --git a/src/test/overlay/short_read_test.cpp b/src/test/overlay/short_read_test.cpp index bff0bd82cb..0c8b7f8a43 100644 --- a/src/test/overlay/short_read_test.cpp +++ b/src/test/overlay/short_read_test.cpp @@ -56,7 +56,7 @@ private: using boost::asio::buffer; using boost::asio::buffer_copy; using boost::asio::buffer_size; - boost::asio::const_buffer buf(s.data(), s.size()); + boost::asio::const_buffer const buf(s.data(), s.size()); sb.commit(buffer_copy(sb.prepare(buffer_size(buf)), buf)); } @@ -100,14 +100,14 @@ private: void add(std::shared_ptr const& child) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); list_.emplace(child.get(), child); } void remove(Child* child) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); list_.erase(child); if (list_.empty()) cond_.notify_one(); @@ -118,7 +118,7 @@ private: { std::vector> v; { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); v.reserve(list_.size()); if (closed_) return; @@ -180,7 +180,10 @@ private: close() override { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&Acceptor::close, shared_from_this())); + { + post(strand_, std::bind(&Acceptor::close, shared_from_this())); + return; + } acceptor_.close(); } @@ -210,7 +213,10 @@ private: on_accept(error_code ec) { if (ec) - return fail("accept", ec); + { + fail("accept", ec); + return; + } auto const p = std::make_shared(server_, std::move(socket_)); server_.add(p); p->run(); @@ -248,7 +254,10 @@ private: close() override { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&Connection::close, shared_from_this())); + { + post(strand_, std::bind(&Connection::close, shared_from_this())); + return; + } if (socket_.is_open()) { socket_.close(); @@ -289,7 +298,10 @@ private: if (ec == boost::asio::error::operation_aborted) return; if (ec) - return fail("timer", ec); + { + fail("timer", ec); + return; + } test_.log << "[server] timeout" << std::endl; socket_.close(); } @@ -298,7 +310,10 @@ private: on_handshake(error_code ec) { if (ec) - return fail("handshake", ec); + { + fail("handshake", ec); + return; + } #if 1 boost::asio::async_read_until( stream_, @@ -322,13 +337,17 @@ private: if (ec == boost::asio::error::eof) { server_.test_.log << "[server] read: EOF" << std::endl; - return stream_.async_shutdown(bind_executor( + stream_.async_shutdown(bind_executor( strand_, std::bind( &Connection::on_shutdown, shared_from_this(), std::placeholders::_1))); + return; } if (ec) - return fail("read", ec); + { + fail("read", ec); + return; + } buf_.commit(bytes_transferred); buf_.consume(bytes_transferred); @@ -350,7 +369,10 @@ private: { buf_.consume(bytes_transferred); if (ec) - return fail("write", ec); + { + fail("write", ec); + return; + } stream_.async_shutdown(bind_executor( strand_, std::bind( @@ -361,7 +383,10 @@ private: on_shutdown(error_code ec) { if (ec) - return fail("shutdown", ec); + { + fail("shutdown", ec); + return; + } socket_.close(); timer_.cancel(); } @@ -422,7 +447,10 @@ private: close() override { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&Connection::close, shared_from_this())); + { + post(strand_, std::bind(&Connection::close, shared_from_this())); + return; + } if (socket_.is_open()) { socket_.close(); @@ -463,7 +491,10 @@ private: if (ec == boost::asio::error::operation_aborted) return; if (ec) - return fail("timer", ec); + { + fail("timer", ec); + return; + } test_.log << "[client] timeout"; socket_.close(); } @@ -472,7 +503,10 @@ private: on_connect(error_code ec) { if (ec) - return fail("connect", ec); + { + fail("connect", ec); + return; + } stream_.async_handshake( stream_type::client, bind_executor( @@ -485,7 +519,10 @@ private: on_handshake(error_code ec) { if (ec) - return fail("handshake", ec); + { + fail("handshake", ec); + return; + } write(buf_, "HELLO\n"); #if 1 @@ -512,7 +549,10 @@ private: { buf_.consume(bytes_transferred); if (ec) - return fail("write", ec); + { + fail("write", ec); + return; + } #if 1 boost::asio::async_read_until( stream_, @@ -537,7 +577,10 @@ private: on_read(error_code ec, std::size_t bytes_transferred) { if (ec) - return fail("read", ec); + { + fail("read", ec); + return; + } buf_.commit(bytes_transferred); stream_.async_shutdown(bind_executor( strand_, @@ -549,7 +592,10 @@ private: on_shutdown(error_code ec) { if (ec) - return fail("shutdown", ec); + { + fail("shutdown", ec); + return; + } socket_.close(); timer_.cancel(); } @@ -590,7 +636,7 @@ public: void run() override { - Server s(*this); + Server const s(*this); Client c(*this, s.endpoint()); c.wait(); pass(); diff --git a/src/test/overlay/tx_reduce_relay_test.cpp b/src/test/overlay/tx_reduce_relay_test.cpp index e3facb2f76..abb1632858 100644 --- a/src/test/overlay/tx_reduce_relay_test.cpp +++ b/src/test/overlay/tx_reduce_relay_test.cpp @@ -53,16 +53,24 @@ private: BEAST_EXPECT(c.TX_REDUCE_RELAY_MIN_PEERS == min); BEAST_EXPECT(c.TX_RELAY_PERCENTAGE == pct); if (success) + { pass(); + } else + { fail(); + } } catch (...) { if (success) + { fail(); + } else + { pass(); + } } }; @@ -143,19 +151,19 @@ private: void addPeer(jtx::Env& env, std::vector>& peers, std::uint16_t& nDisabled) { - auto& overlay = dynamic_cast(env.app().overlay()); + auto& overlay = dynamic_cast(env.app().getOverlay()); boost::beast::http::request request; (nDisabled == 0) - ? (void)request.insert( - "X-Protocol-Ctl", makeFeaturesRequestHeader(false, false, true, false)) + ? request.insert("X-Protocol-Ctl", makeFeaturesRequestHeader(false, false, true, false)) : (void)nDisabled--; auto stream_ptr = std::make_unique( socket_type(std::forward(env.app().getIOContext())), *context_); - beast::IP::Endpoint local(boost::asio::ip::make_address("172.1.1." + std::to_string(lid_))); - beast::IP::Endpoint remote( + beast::IP::Endpoint const local( + boost::asio::ip::make_address("172.1.1." + std::to_string(lid_))); + beast::IP::Endpoint const remote( boost::asio::ip::make_address("172.1.1." + std::to_string(rid_))); - PublicKey key(std::get<0>(randomKeyPair(KeyType::ed25519))); + PublicKey const key(std::get<0>(randomKeyPair(KeyType::ed25519))); auto consumer = overlay.resourceManager().newInboundEndpoint(remote); auto [slot, _] = overlay.peerFinder().new_inbound_slot(local, remote); auto const peer = std::make_shared( @@ -209,7 +217,7 @@ private: m.set_rawtransaction(s.data(), s.size()); m.set_deferred(false); m.set_status(protocol::TransactionStatus::tsNEW); - env.app().overlay().relay(uint256{0}, m, toSkip); + env.app().getOverlay().relay(uint256{0}, m, toSkip); BEAST_EXPECT(PeerTest::sendTx_ == expectRelay && PeerTest::queueTx_ == expectQueue); } } @@ -217,7 +225,7 @@ private: void run() override { - bool log = false; + bool const log = false; std::set skip = {0, 1, 2, 3, 4}; testConfig(log); // relay to all peers, no hash queue diff --git a/src/test/peerfinder/Livecache_test.cpp b/src/test/peerfinder/Livecache_test.cpp index a69a300686..d12da84ffd 100644 --- a/src/test/peerfinder/Livecache_test.cpp +++ b/src/test/peerfinder/Livecache_test.cpp @@ -30,10 +30,10 @@ public: // Add the address as an endpoint template - inline void + void add(beast::IP::Endpoint ep, C& c, std::uint32_t hops = 0) { - Endpoint cep{ep, hops}; + Endpoint const cep{ep, hops}; c.insert(cep); } diff --git a/src/test/peerfinder/PeerFinder_test.cpp b/src/test/peerfinder/PeerFinder_test.cpp index 038ed438f0..2726604c72 100644 --- a/src/test/peerfinder/PeerFinder_test.cpp +++ b/src/test/peerfinder/PeerFinder_test.cpp @@ -391,7 +391,7 @@ public: testcase(test); - std::string toLoad = ""; + std::string toLoad; int max = 0; if (maxPeers) { @@ -411,7 +411,7 @@ public: (c.PEERS_MAX == max && c.PEERS_IN_MAX == 0 && c.PEERS_OUT_MAX == 0) || (c.PEERS_IN_MAX == *maxIn && c.PEERS_OUT_MAX == *maxOut)); - Config config = Config::makeConfig(c, port, false, 0); + Config const config = Config::makeConfig(c, port, false, 0); Counts counts; counts.onConfig(config); @@ -469,32 +469,32 @@ public: pass(); } }; - run(R"rippleConfig( + run(R"xrpldConfig( [peers_in_max] 100 -)rippleConfig"); - run(R"rippleConfig( +)xrpldConfig"); + run(R"xrpldConfig( [peers_out_max] 100 -)rippleConfig"); - run(R"rippleConfig( +)xrpldConfig"); + run(R"xrpldConfig( [peers_in_max] 100 [peers_out_max] 5 -)rippleConfig"); - run(R"rippleConfig( +)xrpldConfig"); + run(R"xrpldConfig( [peers_in_max] 1001 [peers_out_max] 10 -)rippleConfig"); - run(R"rippleConfig( +)xrpldConfig"); + run(R"xrpldConfig( [peers_in_max] 10 [peers_out_max] 1001 -)rippleConfig"); +)xrpldConfig"); } void diff --git a/src/test/protocol/BuildInfo_test.cpp b/src/test/protocol/BuildInfo_test.cpp index d9810e93ea..589eb00637 100644 --- a/src/test/protocol/BuildInfo_test.cpp +++ b/src/test/protocol/BuildInfo_test.cpp @@ -53,13 +53,13 @@ public: } void - testIsRippledVersion() + testIsXrpldVersion() { - testcase("IsRippledVersion"); + testcase("IsXrpldVersion"); auto vFF = 0xFFFF'FFFF'FFFF'FFFFLLU; - BEAST_EXPECT(!BuildInfo::isRippledVersion(vFF)); - auto vRippled = 0x183B'0000'0000'0000LLU; - BEAST_EXPECT(BuildInfo::isRippledVersion(vRippled)); + BEAST_EXPECT(!BuildInfo::isXrpldVersion(vFF)); + auto vXrpld = 0x183B'0000'0000'0000LLU; + BEAST_EXPECT(BuildInfo::isXrpldVersion(vXrpld)); } void @@ -83,7 +83,7 @@ public: run() override { testEncodeSoftwareVersion(); - testIsRippledVersion(); + testIsXrpldVersion(); testIsNewerVersion(); } }; diff --git a/src/test/protocol/Hooks_test.cpp b/src/test/protocol/Hooks_test.cpp index f4b33f7e4d..6f08517c0f 100644 --- a/src/test/protocol/Hooks_test.cpp +++ b/src/test/protocol/Hooks_test.cpp @@ -11,7 +11,7 @@ class Hooks_test : public beast::unit_test::suite { /** * This unit test was requested here: - * https://github.com/ripple/rippled/pull/4089#issuecomment-1050274539 + * https://github.com/XRPLF/rippled/pull/4089#issuecomment-1050274539 * These are tests that exercise facilities that are reserved for when Hooks * is merged in the future. **/ @@ -23,7 +23,7 @@ class Hooks_test : public beast::unit_test::suite using namespace test::jtx; - std::vector> fields_to_test = { + std::vector> const fields_to_test = { sfHookResult, sfHookStateChangeCount, sfHookEmitCount, @@ -116,7 +116,7 @@ class Hooks_test : public beast::unit_test::suite } case STI_UINT256: { - uint256 u = uint256::fromVoid( + uint256 const u = uint256::fromVoid( "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBE" "EFDEADBEEF"); dummy.setFieldH256(f, u); @@ -126,7 +126,7 @@ class Hooks_test : public beast::unit_test::suite } case STI_VL: { - std::vector v{1, 2, 3}; + std::vector const v{1, 2, 3}; dummy.setFieldVL(f, v); BEAST_EXPECT(dummy.getFieldVL(f) == v); BEAST_EXPECT(dummy.isFieldPresent(f)); @@ -134,7 +134,9 @@ class Hooks_test : public beast::unit_test::suite } case STI_ACCOUNT: { - AccountID id = *parseBase58("rwfSjJNK2YQuN64bSWn7T2eY9FJAyAPYJT"); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + AccountID const id = + *parseBase58("rwfSjJNK2YQuN64bSWn7T2eY9FJAyAPYJT"); dummy.setAccountID(f, id); BEAST_EXPECT(dummy.getAccountID(f) == id); BEAST_EXPECT(dummy.isFieldPresent(f)); diff --git a/src/test/protocol/InnerObjectFormats_test.cpp b/src/test/protocol/InnerObjectFormats_test.cpp index c06524b90e..2961e90db7 100644 --- a/src/test/protocol/InnerObjectFormats_test.cpp +++ b/src/test/protocol/InnerObjectFormats_test.cpp @@ -155,7 +155,7 @@ public: using namespace InnerObjectFormatsUnitTestDetail; // Instantiate a jtx::Env so debugLog writes are exercised. - test::jtx::Env env(*this); + test::jtx::Env const env(*this); for (auto const& test : testArray) { @@ -166,7 +166,7 @@ public: Throw( "Internal InnerObjectFormatsParsedJSON error. Bad JSON."); } - STParsedJSONObject parsed("request", req); + STParsedJSONObject const parsed("request", req); bool const noObj = !parsed.object.has_value(); if (noObj == test.expectFail) { diff --git a/src/test/protocol/Issue_test.cpp b/src/test/protocol/Issue_test.cpp index 2321da4a6e..eddaf1c6d8 100644 --- a/src/test/protocol/Issue_test.cpp +++ b/src/test/protocol/Issue_test.cpp @@ -42,7 +42,7 @@ public: BEAST_EXPECT(u3 >= u2); BEAST_EXPECT(u3 > u2); - std::hash hash; + std::hash const hash; BEAST_EXPECT(hash(u1) == hash(u1)); BEAST_EXPECT(hash(u2) == hash(u2)); @@ -83,7 +83,7 @@ public: BEAST_EXPECT(Issue(c1, i3) >= Issue(c1, i2)); BEAST_EXPECT(Issue(c1, i3) > Issue(c1, i2)); - std::hash hash; + std::hash const hash; BEAST_EXPECT(hash(Issue(c1, i1)) == hash(Issue(c1, i1))); BEAST_EXPECT(hash(Issue(c1, i2)) == hash(Issue(c1, i2))); @@ -394,10 +394,10 @@ public: Currency const c3(3); AccountID const i3(3); - Issue a1(c1, i1); - Issue a2(c1, i2); - Issue a3(c2, i2); - Issue a4(c3, i2); + Issue const a1(c1, i1); + Issue const a2(c1, i2); + Issue const a3(c2, i2); + Issue const a4(c3, i2); uint256 const domain1{1}; uint256 const domain2{2}; @@ -477,7 +477,7 @@ public: BEAST_EXPECT(Book(a3, a4, domain2) > Book(a2, a3, domain1)); } - std::hash hash; + std::hash const hash; // log << std::hex << hash (Book (a1, a2)); // log << std::hex << hash (Book (a1, a2)); diff --git a/src/test/protocol/MultiApiJson_test.cpp b/src/test/protocol/MultiApiJson_test.cpp index bbd6ec1a07..5aafa19771 100644 --- a/src/test/protocol/MultiApiJson_test.cpp +++ b/src/test/protocol/MultiApiJson_test.cpp @@ -236,7 +236,7 @@ struct MultiApiJson_test : beast::unit_test::suite std::forward(v).visit(), // [](auto...) {}); }; - }(std::move(std::as_const(s1)))); + }(std::move(std::as_const(s1)))); // NOLINT(performance-move-const-arg) } { @@ -830,6 +830,7 @@ struct MultiApiJson_test : beast::unit_test::suite // Rvalue MultivarJson visitor only binds to regular reference static_assert([](auto&& v) { + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) return !requires { std::forward(v).visit(1, [](Json::Value&&) {}); }; }(std::move(s1))); static_assert([](auto&& v) { @@ -846,6 +847,7 @@ struct MultiApiJson_test : beast::unit_test::suite }; }(std::move(s1))); static_assert([](auto&& v) { + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) return !requires { std::forward(v).visit()(1, [](Json::Value&&) {}); }; }(std::move(s1))); static_assert([](auto&& v) { @@ -865,22 +867,22 @@ struct MultiApiJson_test : beast::unit_test::suite return !requires { std::forward(v).visit(1, [](Json::Value const&&) {}); }; - }(std::move(std::as_const(s1)))); + }(std::move(std::as_const(s1)))); // NOLINT(performance-move-const-arg) static_assert([](auto&& v) { return requires { std::forward(v).visit(1, [](Json::Value const&) {}); }; - }(std::move(std::as_const(s1)))); + }(std::move(std::as_const(s1)))); // NOLINT(performance-move-const-arg) static_assert([](auto&& v) { return !requires { std::forward(v).visit()(1, [](Json::Value const&&) {}); }; - }(std::move(std::as_const(s1)))); + }(std::move(std::as_const(s1)))); // NOLINT(performance-move-const-arg) static_assert([](auto&& v) { return requires { std::forward(v).visit()(1, [](Json::Value const&) {}); }; - }(std::move(std::as_const(s1)))); + }(std::move(std::as_const(s1)))); // NOLINT(performance-move-const-arg) // Missing const static_assert([](auto&& v) { diff --git a/src/test/protocol/PublicKey_test.cpp b/src/test/protocol/PublicKey_test.cpp index 9a7808db7e..79ab589404 100644 --- a/src/test/protocol/PublicKey_test.cpp +++ b/src/test/protocol/PublicKey_test.cpp @@ -17,7 +17,7 @@ public: { struct Table { - int val[256]; + int val[256]{}; Table() { std::fill(val, val + 256, 0); @@ -46,7 +46,7 @@ public: } } - blob + static blob sig(std::string const& hex) { blob b; @@ -54,7 +54,7 @@ public: return b; } - bool + static bool check(std::optional answer, std::string const& s) { return ecdsaCanonicality(makeSlice(sig(s))) == answer; @@ -304,7 +304,7 @@ public: auto s = good; // Remove all characters from the string in random order: - std::hash r; + std::hash const r; while (!s.empty()) { @@ -370,7 +370,8 @@ public: auto const skj = parseBase58(TokenType::NodePublic, sj); BEAST_EXPECT(skj && (keys[j] == *skj)); - BEAST_EXPECT((*ski == *skj) == (i == j)); + BEAST_EXPECT( + (*ski == *skj) == (i == j)); // NOLINT(bugprone-unchecked-optional-access) } } } @@ -389,7 +390,7 @@ public: TokenType::NodePublic, "n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9"); BEAST_EXPECT(pk2); - BEAST_EXPECT(pk1 == *pk2); + BEAST_EXPECT(pk1 == *pk2); // NOLINT(bugprone-unchecked-optional-access) } testBase58(KeyType::secp256k1); @@ -405,7 +406,7 @@ public: TokenType::NodePublic, "nHUeeJCSY2dM71oxM8Cgjouf5ekTuev2mwDpc374aLMxzDLXNmjf"); BEAST_EXPECT(pk2); - BEAST_EXPECT(pk1 == *pk2); + BEAST_EXPECT(pk1 == *pk2); // NOLINT(bugprone-unchecked-optional-access) } testBase58(KeyType::ed25519); @@ -420,7 +421,7 @@ public: KeyType::secp256k1, generateSecretKey(KeyType::secp256k1, generateSeed("masterpassphrase"))); - PublicKey pk2(pk1); + PublicKey const pk2(pk1); BEAST_EXPECT(pk1 == pk2); BEAST_EXPECT(pk2 == pk1); diff --git a/src/test/protocol/Quality_test.cpp b/src/test/protocol/Quality_test.cpp index 1dbbbfdf3d..f421f98c94 100644 --- a/src/test/protocol/Quality_test.cpp +++ b/src/test/protocol/Quality_test.cpp @@ -66,7 +66,7 @@ public: { // 1 in, 1 out: - Quality q(Amounts(amount(1), amount(1))); + Quality const q(Amounts(amount(1), amount(1))); ceil_in( q, @@ -95,7 +95,7 @@ public: { // 1 in, 2 out: - Quality q(Amounts(amount(1), amount(2))); + Quality const q(Amounts(amount(1), amount(2))); ceil_in( q, @@ -124,7 +124,7 @@ public: { // 2 in, 1 out: - Quality q(Amounts(amount(2), amount(1))); + Quality const q(Amounts(amount(2), amount(1))); ceil_in( q, @@ -159,7 +159,7 @@ public: { // 1 in, 1 out: - Quality q(Amounts(amount(1), amount(1))); + Quality const q(Amounts(amount(1), amount(1))); ceil_out( q, @@ -188,7 +188,7 @@ public: { // 1 in, 2 out: - Quality q(Amounts(amount(1), amount(2))); + Quality const q(Amounts(amount(1), amount(2))); ceil_out( q, @@ -217,7 +217,7 @@ public: { // 2 in, 1 out: - Quality q(Amounts(amount(2), amount(1))); + Quality const q(Amounts(amount(2), amount(1))); ceil_out( q, @@ -251,7 +251,7 @@ public: testcase("raw"); { - Quality q(0x5d048191fb9130daull); // 126836389.7680090 + Quality const q(0x5d048191fb9130daull); // 126836389.7680090 Amounts const value( amount(349469768), // 349.469768 XRP raw(2755280000000000ull, -15)); // 2.75528 @@ -266,7 +266,7 @@ public: { testcase("round"); - Quality q(0x59148191fb913522ull); // 57719.63525051682 + Quality const q(0x59148191fb913522ull); // 57719.63525051682 BEAST_EXPECT(q.round(3).rate().getText() == "57800"); BEAST_EXPECT(q.round(4).rate().getText() == "57720"); BEAST_EXPECT(q.round(5).rate().getText() == "57720"); diff --git a/src/test/protocol/STAccount_test.cpp b/src/test/protocol/STAccount_test.cpp index 76b633df80..3b12605a92 100644 --- a/src/test/protocol/STAccount_test.cpp +++ b/src/test/protocol/STAccount_test.cpp @@ -12,7 +12,7 @@ struct STAccount_test : public beast::unit_test::suite // Test default constructor. STAccount const defaultAcct; BEAST_EXPECT(defaultAcct.getSType() == STI_ACCOUNT); - BEAST_EXPECT(defaultAcct.getText() == ""); + BEAST_EXPECT(defaultAcct.getText().empty()); BEAST_EXPECT(defaultAcct.isDefault() == true); BEAST_EXPECT(defaultAcct.value() == AccountID{}); { @@ -38,7 +38,7 @@ struct STAccount_test : public beast::unit_test::suite // Test constructor from SField. STAccount const sfAcct{sfAccount}; BEAST_EXPECT(sfAcct.getSType() == STI_ACCOUNT); - BEAST_EXPECT(sfAcct.getText() == ""); + BEAST_EXPECT(sfAcct.getText().empty()); BEAST_EXPECT(sfAcct.isDefault()); BEAST_EXPECT(sfAcct.value() == AccountID{}); BEAST_EXPECT(sfAcct.isEquivalent(defaultAcct)); @@ -106,7 +106,7 @@ struct STAccount_test : public beast::unit_test::suite auto const s = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; if (auto const parsed = parseBase58(s); BEAST_EXPECT(parsed)) { - BEAST_EXPECT(toBase58(*parsed) == s); + BEAST_EXPECT(toBase58(*parsed) == s); // NOLINT(bugprone-unchecked-optional-access) } { diff --git a/src/test/protocol/STAmount_test.cpp b/src/test/protocol/STAmount_test.cpp index c9382d508a..2c2e005146 100644 --- a/src/test/protocol/STAmount_test.cpp +++ b/src/test/protocol/STAmount_test.cpp @@ -21,14 +21,14 @@ public: } //-------------------------------------------------------------------------- - STAmount + static STAmount roundSelf(STAmount const& amount) { if (amount.native()) return amount; std::uint64_t mantissa = amount.mantissa(); - std::uint64_t valueDigits = mantissa % 1000000000; + std::uint64_t const valueDigits = mantissa % 1000000000; if (valueDigits == 1) { @@ -67,15 +67,15 @@ public: roundTest(int n, int d, int m) { // check STAmount rounding - STAmount num(noIssue(), n); - STAmount den(noIssue(), d); - STAmount mul(noIssue(), m); - STAmount quot = divide(STAmount(n), STAmount(d), noIssue()); - STAmount res = roundSelf(multiply(quot, mul, noIssue())); + STAmount const num(noIssue(), n); + STAmount const den(noIssue(), d); + STAmount const mul(noIssue(), m); + STAmount const quot = divide(STAmount(n), STAmount(d), noIssue()); + STAmount const res = roundSelf(multiply(quot, mul, noIssue())); BEAST_EXPECT(!res.native()); - STAmount cmp(noIssue(), (n * m) / d); + STAmount const cmp(noIssue(), (n * m) / d); BEAST_EXPECT(!cmp.native()); @@ -93,13 +93,14 @@ public: void mulTest(int a, int b) { - STAmount aa(noIssue(), a); - STAmount bb(noIssue(), b); - STAmount prod1(multiply(aa, bb, noIssue())); + STAmount const aa(noIssue(), a); + STAmount const bb(noIssue(), b); + STAmount const prod1(multiply(aa, bb, noIssue())); BEAST_EXPECT(!prod1.native()); - STAmount prod2(noIssue(), static_cast(a) * static_cast(b)); + STAmount const prod2( + noIssue(), static_cast(a) * static_cast(b)); if (prod1 != prod2) { @@ -196,7 +197,11 @@ public: testNativeCurrency() { testcase("native currency"); - STAmount zeroSt, one(1), hundred(100); + + STAmount const zeroSt; + STAmount const one(1); + STAmount const hundred(100); + // VFALCO NOTE Why repeat "STAmount fail" so many times?? unexpected(serializeAndDeserialize(zeroSt) != zeroSt, "STAmount fail"); unexpected(serializeAndDeserialize(one) != one, "STAmount fail"); @@ -206,60 +211,60 @@ public: unexpected(zeroSt != beast::zero, "STAmount fail"); unexpected(one == beast::zero, "STAmount fail"); unexpected(hundred == beast::zero, "STAmount fail"); - unexpected((zeroSt < zeroSt), "STAmount fail"); + unexpected((zeroSt < zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected(!(zeroSt < one), "STAmount fail"); unexpected(!(zeroSt < hundred), "STAmount fail"); unexpected((one < zeroSt), "STAmount fail"); - unexpected((one < one), "STAmount fail"); + unexpected((one < one), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected(!(one < hundred), "STAmount fail"); unexpected((hundred < zeroSt), "STAmount fail"); unexpected((hundred < one), "STAmount fail"); - unexpected((hundred < hundred), "STAmount fail"); - unexpected((zeroSt > zeroSt), "STAmount fail"); + unexpected((hundred < hundred), "STAmount fail"); // NOLINT(misc-redundant-expression) + unexpected((zeroSt > zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected((zeroSt > one), "STAmount fail"); unexpected((zeroSt > hundred), "STAmount fail"); unexpected(!(one > zeroSt), "STAmount fail"); - unexpected((one > one), "STAmount fail"); + unexpected((one > one), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected((one > hundred), "STAmount fail"); unexpected(!(hundred > zeroSt), "STAmount fail"); unexpected(!(hundred > one), "STAmount fail"); - unexpected((hundred > hundred), "STAmount fail"); - unexpected(!(zeroSt <= zeroSt), "STAmount fail"); + unexpected((hundred > hundred), "STAmount fail"); // NOLINT(misc-redundant-expression) + unexpected(!(zeroSt <= zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected(!(zeroSt <= one), "STAmount fail"); unexpected(!(zeroSt <= hundred), "STAmount fail"); unexpected((one <= zeroSt), "STAmount fail"); - unexpected(!(one <= one), "STAmount fail"); + unexpected(!(one <= one), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected(!(one <= hundred), "STAmount fail"); unexpected((hundred <= zeroSt), "STAmount fail"); unexpected((hundred <= one), "STAmount fail"); - unexpected(!(hundred <= hundred), "STAmount fail"); - unexpected(!(zeroSt >= zeroSt), "STAmount fail"); + unexpected(!(hundred <= hundred), "STAmount fail"); // NOLINT(misc-redundant-expression) + unexpected(!(zeroSt >= zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected((zeroSt >= one), "STAmount fail"); unexpected((zeroSt >= hundred), "STAmount fail"); unexpected(!(one >= zeroSt), "STAmount fail"); - unexpected(!(one >= one), "STAmount fail"); + unexpected(!(one >= one), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected((one >= hundred), "STAmount fail"); unexpected(!(hundred >= zeroSt), "STAmount fail"); unexpected(!(hundred >= one), "STAmount fail"); - unexpected(!(hundred >= hundred), "STAmount fail"); - unexpected(!(zeroSt == zeroSt), "STAmount fail"); + unexpected(!(hundred >= hundred), "STAmount fail"); // NOLINT(misc-redundant-expression) + unexpected(!(zeroSt == zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected((zeroSt == one), "STAmount fail"); unexpected((zeroSt == hundred), "STAmount fail"); unexpected((one == zeroSt), "STAmount fail"); - unexpected(!(one == one), "STAmount fail"); + unexpected(!(one == one), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected((one == hundred), "STAmount fail"); unexpected((hundred == zeroSt), "STAmount fail"); unexpected((hundred == one), "STAmount fail"); - unexpected(!(hundred == hundred), "STAmount fail"); - unexpected((zeroSt != zeroSt), "STAmount fail"); + unexpected(!(hundred == hundred), "STAmount fail"); // NOLINT(misc-redundant-expression) + unexpected((zeroSt != zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected(!(zeroSt != one), "STAmount fail"); unexpected(!(zeroSt != hundred), "STAmount fail"); unexpected(!(one != zeroSt), "STAmount fail"); - unexpected((one != one), "STAmount fail"); + unexpected((one != one), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected(!(one != hundred), "STAmount fail"); unexpected(!(hundred != zeroSt), "STAmount fail"); unexpected(!(hundred != one), "STAmount fail"); - unexpected((hundred != hundred), "STAmount fail"); + unexpected((hundred != hundred), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected(STAmount().getText() != "0", "STAmount fail"); unexpected(STAmount(31).getText() != "31", "STAmount fail"); unexpected(STAmount(310).getText() != "310", "STAmount fail"); @@ -279,7 +284,11 @@ public: testCustomCurrency() { testcase("custom currency"); - STAmount zeroSt(noIssue()), one(noIssue(), 1), hundred(noIssue(), 100); + + STAmount const zeroSt(noIssue()); + STAmount const one(noIssue(), 1); + STAmount const hundred(noIssue(), 100); + unexpected(serializeAndDeserialize(zeroSt) != zeroSt, "STAmount fail"); unexpected(serializeAndDeserialize(one) != one, "STAmount fail"); unexpected(serializeAndDeserialize(hundred) != hundred, "STAmount fail"); @@ -288,60 +297,60 @@ public: unexpected(zeroSt != beast::zero, "STAmount fail"); unexpected(one == beast::zero, "STAmount fail"); unexpected(hundred == beast::zero, "STAmount fail"); - unexpected((zeroSt < zeroSt), "STAmount fail"); + unexpected((zeroSt < zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected(!(zeroSt < one), "STAmount fail"); unexpected(!(zeroSt < hundred), "STAmount fail"); unexpected((one < zeroSt), "STAmount fail"); - unexpected((one < one), "STAmount fail"); + unexpected((one < one), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected(!(one < hundred), "STAmount fail"); unexpected((hundred < zeroSt), "STAmount fail"); unexpected((hundred < one), "STAmount fail"); - unexpected((hundred < hundred), "STAmount fail"); - unexpected((zeroSt > zeroSt), "STAmount fail"); + unexpected((hundred < hundred), "STAmount fail"); // NOLINT(misc-redundant-expression) + unexpected((zeroSt > zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected((zeroSt > one), "STAmount fail"); unexpected((zeroSt > hundred), "STAmount fail"); unexpected(!(one > zeroSt), "STAmount fail"); - unexpected((one > one), "STAmount fail"); + unexpected((one > one), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected((one > hundred), "STAmount fail"); unexpected(!(hundred > zeroSt), "STAmount fail"); unexpected(!(hundred > one), "STAmount fail"); - unexpected((hundred > hundred), "STAmount fail"); - unexpected(!(zeroSt <= zeroSt), "STAmount fail"); + unexpected((hundred > hundred), "STAmount fail"); // NOLINT(misc-redundant-expression) + unexpected(!(zeroSt <= zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected(!(zeroSt <= one), "STAmount fail"); unexpected(!(zeroSt <= hundred), "STAmount fail"); unexpected((one <= zeroSt), "STAmount fail"); - unexpected(!(one <= one), "STAmount fail"); + unexpected(!(one <= one), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected(!(one <= hundred), "STAmount fail"); unexpected((hundred <= zeroSt), "STAmount fail"); unexpected((hundred <= one), "STAmount fail"); - unexpected(!(hundred <= hundred), "STAmount fail"); - unexpected(!(zeroSt >= zeroSt), "STAmount fail"); + unexpected(!(hundred <= hundred), "STAmount fail"); // NOLINT(misc-redundant-expression) + unexpected(!(zeroSt >= zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected((zeroSt >= one), "STAmount fail"); unexpected((zeroSt >= hundred), "STAmount fail"); unexpected(!(one >= zeroSt), "STAmount fail"); - unexpected(!(one >= one), "STAmount fail"); + unexpected(!(one >= one), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected((one >= hundred), "STAmount fail"); unexpected(!(hundred >= zeroSt), "STAmount fail"); unexpected(!(hundred >= one), "STAmount fail"); - unexpected(!(hundred >= hundred), "STAmount fail"); - unexpected(!(zeroSt == zeroSt), "STAmount fail"); + unexpected(!(hundred >= hundred), "STAmount fail"); // NOLINT(misc-redundant-expression) + unexpected(!(zeroSt == zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected((zeroSt == one), "STAmount fail"); unexpected((zeroSt == hundred), "STAmount fail"); unexpected((one == zeroSt), "STAmount fail"); - unexpected(!(one == one), "STAmount fail"); + unexpected(!(one == one), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected((one == hundred), "STAmount fail"); unexpected((hundred == zeroSt), "STAmount fail"); unexpected((hundred == one), "STAmount fail"); - unexpected(!(hundred == hundred), "STAmount fail"); - unexpected((zeroSt != zeroSt), "STAmount fail"); + unexpected(!(hundred == hundred), "STAmount fail"); // NOLINT(misc-redundant-expression) + unexpected((zeroSt != zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected(!(zeroSt != one), "STAmount fail"); unexpected(!(zeroSt != hundred), "STAmount fail"); unexpected(!(one != zeroSt), "STAmount fail"); - unexpected((one != one), "STAmount fail"); + unexpected((one != one), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected(!(one != hundred), "STAmount fail"); unexpected(!(hundred != zeroSt), "STAmount fail"); unexpected(!(hundred != one), "STAmount fail"); - unexpected((hundred != hundred), "STAmount fail"); + unexpected((hundred != hundred), "STAmount fail"); // NOLINT(misc-redundant-expression) unexpected(STAmount(noIssue()).getText() != "0", "STAmount fail"); unexpected(STAmount(noIssue(), 31).getText() != "31", "STAmount fail"); unexpected(STAmount(noIssue(), 31, 1).getText() != "310", "STAmount fail"); @@ -382,7 +391,8 @@ public: divide(STAmount(noIssue(), 60), STAmount(noIssue(), 3), xrpIssue()).getText() != "20", "STAmount divide fail"); - STAmount a1(noIssue(), 60), a2(noIssue(), 10, -1); + STAmount const a1(noIssue(), 60); + STAmount const a2(noIssue(), 10, -1); unexpected( divide(a2, a1, noIssue()) != amountFromQuality(getRate(a1, a2)), @@ -464,14 +474,14 @@ public: { testcase("underflow"); - STAmount bigNative(STAmount::cMaxNative / 2); - STAmount bigValue( + STAmount const bigNative(STAmount::cMaxNative / 2); + STAmount const bigValue( noIssue(), (STAmount::cMinValue + STAmount::cMaxValue) / 2, STAmount::cMaxOffset - 1); - STAmount smallValue( + STAmount const smallValue( noIssue(), (STAmount::cMinValue + STAmount::cMaxValue) / 2, STAmount::cMinOffset + 1); - STAmount zeroSt(noIssue(), 0); + STAmount const zeroSt(noIssue(), 0); - STAmount smallXSmall = multiply(smallValue, smallValue, noIssue()); + STAmount const smallXSmall = multiply(smallValue, smallValue, noIssue()); BEAST_EXPECT(smallXSmall == beast::zero); @@ -822,43 +832,43 @@ public: // Adding zero { - STAmount amt1(XRPAmount(0)); - STAmount amt2(XRPAmount(1000)); + STAmount const amt1(XRPAmount(0)); + STAmount const amt2(XRPAmount(1000)); BEAST_EXPECT(canAdd(amt1, amt2) == true); } // Adding zero { - STAmount amt1(XRPAmount(1000)); - STAmount amt2(XRPAmount(0)); + STAmount const amt1(XRPAmount(1000)); + STAmount const amt2(XRPAmount(0)); BEAST_EXPECT(canAdd(amt1, amt2) == true); } // Adding two positive XRP amounts { - STAmount amt1(XRPAmount(500)); - STAmount amt2(XRPAmount(1500)); + STAmount const amt1(XRPAmount(500)); + STAmount const amt2(XRPAmount(1500)); BEAST_EXPECT(canAdd(amt1, amt2) == true); } // Adding two negative XRP amounts { - STAmount amt1(XRPAmount(-500)); - STAmount amt2(XRPAmount(-1500)); + STAmount const amt1(XRPAmount(-500)); + STAmount const amt2(XRPAmount(-1500)); BEAST_EXPECT(canAdd(amt1, amt2) == true); } // Adding a positive and a negative XRP amount { - STAmount amt1(XRPAmount(1000)); - STAmount amt2(XRPAmount(-1000)); + STAmount const amt1(XRPAmount(1000)); + STAmount const amt2(XRPAmount(-1000)); BEAST_EXPECT(canAdd(amt1, amt2) == true); } // Overflow check for max XRP amounts { - STAmount amt1(std::numeric_limits::max()); - STAmount amt2(XRPAmount(1)); + STAmount const amt1(std::numeric_limits::max()); + STAmount const amt2(XRPAmount(1)); BEAST_EXPECT(canAdd(amt1, amt2) == false); } @@ -866,7 +876,7 @@ public: { STAmount amt1(std::numeric_limits::max()); amt1 += XRPAmount(1); - STAmount amt2(XRPAmount(-1)); + STAmount const amt2(XRPAmount(-1)); BEAST_EXPECT(canAdd(amt1, amt2) == false); } } @@ -881,50 +891,50 @@ public: // Adding two IOU amounts { - STAmount amt1(usd, 500); - STAmount amt2(usd, 1500); + STAmount const amt1(usd, 500); + STAmount const amt2(usd, 1500); BEAST_EXPECT(canAdd(amt1, amt2) == true); } // Adding a positive and a negative IOU amount { - STAmount amt1(usd, 1000); - STAmount amt2(usd, -1000); + STAmount const amt1(usd, 1000); + STAmount const amt2(usd, -1000); BEAST_EXPECT(canAdd(amt1, amt2) == true); } // Overflow check for max IOU amounts { - STAmount amt1(usd, std::numeric_limits::max()); - STAmount amt2(usd, 1); + STAmount const amt1(usd, std::numeric_limits::max()); + STAmount const amt2(usd, 1); BEAST_EXPECT(canAdd(amt1, amt2) == false); } // Overflow check for min IOU amounts { - STAmount amt1(usd, std::numeric_limits::min()); - STAmount amt2(usd, -1); + STAmount const amt1(usd, std::numeric_limits::min()); + STAmount const amt2(usd, -1); BEAST_EXPECT(canAdd(amt1, amt2) == false); } // Adding XRP and IOU { - STAmount amt1(XRPAmount(1)); - STAmount amt2(usd, 1); + STAmount const amt1(XRPAmount(1)); + STAmount const amt2(usd, 1); BEAST_EXPECT(canAdd(amt1, amt2) == false); } // Adding different IOU issues (non zero) { - STAmount amt1(usd, 1000); - STAmount amt2(eur, 500); + STAmount const amt1(usd, 1000); + STAmount const amt2(eur, 500); BEAST_EXPECT(canAdd(amt1, amt2) == false); } // Adding different IOU issues (zero) { - STAmount amt1(usd, 0); - STAmount amt2(eur, 500); + STAmount const amt1(usd, 0); + STAmount const amt2(eur, 500); BEAST_EXPECT(canAdd(amt1, amt2) == false); } } @@ -939,43 +949,43 @@ public: // Adding zero { - STAmount amt1(mpt, 0); - STAmount amt2(mpt, 1000); + STAmount const amt1(mpt, 0); + STAmount const amt2(mpt, 1000); BEAST_EXPECT(canAdd(amt1, amt2) == true); } // Adding zero { - STAmount amt1(mpt, 1000); - STAmount amt2(mpt, 0); + STAmount const amt1(mpt, 1000); + STAmount const amt2(mpt, 0); BEAST_EXPECT(canAdd(amt1, amt2) == true); } // Adding two positive MPT amounts { - STAmount amt1(mpt, 500); - STAmount amt2(mpt, 1500); + STAmount const amt1(mpt, 500); + STAmount const amt2(mpt, 1500); BEAST_EXPECT(canAdd(amt1, amt2) == true); } // Adding two negative MPT amounts { - STAmount amt1(mpt, -500); - STAmount amt2(mpt, -1500); + STAmount const amt1(mpt, -500); + STAmount const amt2(mpt, -1500); BEAST_EXPECT(canAdd(amt1, amt2) == true); } // Adding a positive and a negative MPT amount { - STAmount amt1(mpt, 1000); - STAmount amt2(mpt, -1000); + STAmount const amt1(mpt, 1000); + STAmount const amt2(mpt, -1000); BEAST_EXPECT(canAdd(amt1, amt2) == true); } // Overflow check for max MPT amounts { - STAmount amt1(mpt, std::numeric_limits::max()); - STAmount amt2(mpt, 1); + STAmount const amt1(mpt, std::numeric_limits::max()); + STAmount const amt2(mpt, 1); BEAST_EXPECT(canAdd(amt1, amt2) == false); } @@ -985,22 +995,22 @@ public: // Adding MPT and XRP { - STAmount amt1(XRPAmount(1000)); - STAmount amt2(mpt, 1000); + STAmount const amt1(XRPAmount(1000)); + STAmount const amt2(mpt, 1000); BEAST_EXPECT(canAdd(amt1, amt2) == false); } // Adding different MPT issues (non zero) { - STAmount amt1(mpt2, 500); - STAmount amt2(mpt, 500); + STAmount const amt1(mpt2, 500); + STAmount const amt2(mpt, 500); BEAST_EXPECT(canAdd(amt1, amt2) == false); } // Adding different MPT issues (non zero) { - STAmount amt1(mpt2, 0); - STAmount amt2(mpt, 500); + STAmount const amt1(mpt2, 0); + STAmount const amt2(mpt, 500); BEAST_EXPECT(canAdd(amt1, amt2) == false); } } @@ -1012,36 +1022,36 @@ public: // Subtracting zero { - STAmount amt1(XRPAmount(1000)); - STAmount amt2(XRPAmount(0)); + STAmount const amt1(XRPAmount(1000)); + STAmount const amt2(XRPAmount(0)); BEAST_EXPECT(canSubtract(amt1, amt2) == true); } // Subtracting zero { - STAmount amt1(XRPAmount(0)); - STAmount amt2(XRPAmount(1000)); + STAmount const amt1(XRPAmount(0)); + STAmount const amt2(XRPAmount(1000)); BEAST_EXPECT(canSubtract(amt1, amt2) == false); } // Subtracting two positive XRP amounts { - STAmount amt1(XRPAmount(1500)); - STAmount amt2(XRPAmount(500)); + STAmount const amt1(XRPAmount(1500)); + STAmount const amt2(XRPAmount(500)); BEAST_EXPECT(canSubtract(amt1, amt2) == true); } // Subtracting two negative XRP amounts { - STAmount amt1(XRPAmount(-1500)); - STAmount amt2(XRPAmount(-500)); + STAmount const amt1(XRPAmount(-1500)); + STAmount const amt2(XRPAmount(-500)); BEAST_EXPECT(canSubtract(amt1, amt2) == true); } // Subtracting a positive and a negative XRP amount { - STAmount amt1(XRPAmount(1000)); - STAmount amt2(XRPAmount(-1000)); + STAmount const amt1(XRPAmount(1000)); + STAmount const amt2(XRPAmount(-1000)); BEAST_EXPECT(canSubtract(amt1, amt2) == true); } @@ -1049,14 +1059,14 @@ public: { STAmount amt1(std::numeric_limits::max()); amt1 += XRPAmount(1); - STAmount amt2(XRPAmount(1)); + STAmount const amt2(XRPAmount(1)); BEAST_EXPECT(canSubtract(amt1, amt2) == false); } // Overflow check for max XRP amounts { - STAmount amt1(std::numeric_limits::max()); - STAmount amt2(XRPAmount(-1)); + STAmount const amt1(std::numeric_limits::max()); + STAmount const amt2(XRPAmount(-1)); BEAST_EXPECT(canSubtract(amt1, amt2) == false); } } @@ -1070,29 +1080,29 @@ public: // Subtracting two IOU amounts { - STAmount amt1(usd, 1500); - STAmount amt2(usd, 500); + STAmount const amt1(usd, 1500); + STAmount const amt2(usd, 500); BEAST_EXPECT(canSubtract(amt1, amt2) == true); } // Subtracting XRP and IOU { - STAmount amt1(XRPAmount(1000)); - STAmount amt2(usd, 1000); + STAmount const amt1(XRPAmount(1000)); + STAmount const amt2(usd, 1000); BEAST_EXPECT(canSubtract(amt1, amt2) == false); } // Subtracting different IOU issues (non zero) { - STAmount amt1(usd, 1000); - STAmount amt2(eur, 500); + STAmount const amt1(usd, 1000); + STAmount const amt2(eur, 500); BEAST_EXPECT(canSubtract(amt1, amt2) == false); } // Subtracting different IOU issues (zero) { - STAmount amt1(usd, 0); - STAmount amt2(eur, 500); + STAmount const amt1(usd, 0); + STAmount const amt2(eur, 500); BEAST_EXPECT(canSubtract(amt1, amt2) == false); } } @@ -1107,36 +1117,36 @@ public: // Subtracting zero { - STAmount amt1(mpt, 1000); - STAmount amt2(mpt, 0); + STAmount const amt1(mpt, 1000); + STAmount const amt2(mpt, 0); BEAST_EXPECT(canSubtract(amt1, amt2) == true); } // Subtracting zero { - STAmount amt1(mpt, 0); - STAmount amt2(mpt, 1000); + STAmount const amt1(mpt, 0); + STAmount const amt2(mpt, 1000); BEAST_EXPECT(canSubtract(amt1, amt2) == false); } // Subtracting two positive MPT amounts { - STAmount amt1(mpt, 1500); - STAmount amt2(mpt, 500); + STAmount const amt1(mpt, 1500); + STAmount const amt2(mpt, 500); BEAST_EXPECT(canSubtract(amt1, amt2) == true); } // Subtracting two negative MPT amounts { - STAmount amt1(mpt, -1500); - STAmount amt2(mpt, -500); + STAmount const amt1(mpt, -1500); + STAmount const amt2(mpt, -500); BEAST_EXPECT(canSubtract(amt1, amt2) == true); } // Subtracting a positive and a negative MPT amount { - STAmount amt1(mpt, 1000); - STAmount amt2(mpt, -1000); + STAmount const amt1(mpt, 1000); + STAmount const amt2(mpt, -1000); BEAST_EXPECT(canSubtract(amt1, amt2) == true); } @@ -1146,29 +1156,29 @@ public: // Overflow check for max positive MPT amounts (should fail) { - STAmount amt1(mpt, std::numeric_limits::max()); - STAmount amt2(mpt, -2); + STAmount const amt1(mpt, std::numeric_limits::max()); + STAmount const amt2(mpt, -2); BEAST_EXPECT(canSubtract(amt1, amt2) == false); } // Subtracting MPT and XRP { - STAmount amt1(XRPAmount(1000)); - STAmount amt2(mpt, 1000); + STAmount const amt1(XRPAmount(1000)); + STAmount const amt2(mpt, 1000); BEAST_EXPECT(canSubtract(amt1, amt2) == false); } // Subtracting different MPT issues (non zero) { - STAmount amt1(mpt, 1000); - STAmount amt2(mpt2, 500); + STAmount const amt1(mpt, 1000); + STAmount const amt2(mpt2, 500); BEAST_EXPECT(canSubtract(amt1, amt2) == false); } // Subtracting different MPT issues (zero) { - STAmount amt1(mpt, 0); - STAmount amt2(mpt2, 500); + STAmount const amt1(mpt, 0); + STAmount const amt2(mpt2, 500); BEAST_EXPECT(canSubtract(amt1, amt2) == false); } } diff --git a/src/test/protocol/STInteger_test.cpp b/src/test/protocol/STInteger_test.cpp index 937dbd1a7f..4a0204d349 100644 --- a/src/test/protocol/STInteger_test.cpp +++ b/src/test/protocol/STInteger_test.cpp @@ -12,14 +12,14 @@ struct STInteger_test : public beast::unit_test::suite testUInt8() { testcase("UInt8"); - STUInt8 u8(255); + STUInt8 const u8(255); BEAST_EXPECT(u8.value() == 255); BEAST_EXPECT(u8.getText() == "255"); BEAST_EXPECT(u8.getSType() == STI_UINT8); BEAST_EXPECT(u8.getJson(JsonOptions::none) == 255); // there is some special handling for sfTransactionResult - STUInt8 tr(sfTransactionResult, 0); + STUInt8 const tr(sfTransactionResult, 0); BEAST_EXPECT(tr.value() == 0); BEAST_EXPECT( tr.getText() == "The transaction was applied. Only final in a validated ledger."); @@ -27,7 +27,7 @@ struct STInteger_test : public beast::unit_test::suite BEAST_EXPECT(tr.getJson(JsonOptions::none) == "tesSUCCESS"); // invalid transaction result - STUInt8 tr2(sfTransactionResult, 255); + STUInt8 const tr2(sfTransactionResult, 255); BEAST_EXPECT(tr2.value() == 255); BEAST_EXPECT(tr2.getText() == "255"); BEAST_EXPECT(tr2.getSType() == STI_UINT8); @@ -38,21 +38,21 @@ struct STInteger_test : public beast::unit_test::suite testUInt16() { testcase("UInt16"); - STUInt16 u16(65535); + STUInt16 const u16(65535); BEAST_EXPECT(u16.value() == 65535); BEAST_EXPECT(u16.getText() == "65535"); BEAST_EXPECT(u16.getSType() == STI_UINT16); BEAST_EXPECT(u16.getJson(JsonOptions::none) == 65535); // there is some special handling for sfLedgerEntryType - STUInt16 let(sfLedgerEntryType, ltACCOUNT_ROOT); + STUInt16 const let(sfLedgerEntryType, ltACCOUNT_ROOT); BEAST_EXPECT(let.value() == ltACCOUNT_ROOT); BEAST_EXPECT(let.getText() == "AccountRoot"); BEAST_EXPECT(let.getSType() == STI_UINT16); BEAST_EXPECT(let.getJson(JsonOptions::none) == "AccountRoot"); // there is some special handling for sfTransactionType - STUInt16 tlt(sfTransactionType, ttPAYMENT); + STUInt16 const tlt(sfTransactionType, ttPAYMENT); BEAST_EXPECT(tlt.value() == ttPAYMENT); BEAST_EXPECT(tlt.getText() == "Payment"); BEAST_EXPECT(tlt.getSType() == STI_UINT16); @@ -63,19 +63,19 @@ struct STInteger_test : public beast::unit_test::suite testUInt32() { testcase("UInt32"); - STUInt32 u32(4'294'967'295u); + STUInt32 const u32(4'294'967'295u); BEAST_EXPECT(u32.value() == 4'294'967'295u); BEAST_EXPECT(u32.getText() == "4294967295"); BEAST_EXPECT(u32.getSType() == STI_UINT32); BEAST_EXPECT(u32.getJson(JsonOptions::none) == 4'294'967'295u); // there is some special handling for sfPermissionValue - STUInt32 pv(sfPermissionValue, ttPAYMENT + 1); + STUInt32 const pv(sfPermissionValue, ttPAYMENT + 1); BEAST_EXPECT(pv.value() == ttPAYMENT + 1); BEAST_EXPECT(pv.getText() == "Payment"); BEAST_EXPECT(pv.getSType() == STI_UINT32); BEAST_EXPECT(pv.getJson(JsonOptions::none) == "Payment"); - STUInt32 pv2(sfPermissionValue, PaymentMint); + STUInt32 const pv2(sfPermissionValue, PaymentMint); BEAST_EXPECT(pv2.value() == PaymentMint); BEAST_EXPECT(pv2.getText() == "PaymentMint"); BEAST_EXPECT(pv2.getSType() == STI_UINT32); @@ -86,7 +86,7 @@ struct STInteger_test : public beast::unit_test::suite testUInt64() { testcase("UInt64"); - STUInt64 u64(0xFFFFFFFFFFFFFFFFull); + STUInt64 const u64(0xFFFFFFFFFFFFFFFFull); BEAST_EXPECT(u64.value() == 0xFFFFFFFFFFFFFFFFull); BEAST_EXPECT(u64.getText() == "18446744073709551615"); BEAST_EXPECT(u64.getSType() == STI_UINT64); @@ -96,7 +96,7 @@ struct STInteger_test : public beast::unit_test::suite BEAST_EXPECT(jsonVal.isString()); BEAST_EXPECT(jsonVal.asString() == "ffffffffffffffff"); - STUInt64 u64_2(sfMaximumAmount, 0xFFFFFFFFFFFFFFFFull); + STUInt64 const u64_2(sfMaximumAmount, 0xFFFFFFFFFFFFFFFFull); BEAST_EXPECT(u64_2.value() == 0xFFFFFFFFFFFFFFFFull); BEAST_EXPECT(u64_2.getText() == "18446744073709551615"); BEAST_EXPECT(u64_2.getSType() == STI_UINT64); @@ -109,7 +109,7 @@ struct STInteger_test : public beast::unit_test::suite testcase("Int32"); { int const minInt32 = -2147483648; - STInt32 i32(minInt32); + STInt32 const i32(minInt32); BEAST_EXPECT(i32.value() == minInt32); BEAST_EXPECT(i32.getText() == "-2147483648"); BEAST_EXPECT(i32.getSType() == STI_INT32); @@ -118,7 +118,7 @@ struct STInteger_test : public beast::unit_test::suite { int const maxInt32 = 2147483647; - STInt32 i32(maxInt32); + STInt32 const i32(maxInt32); BEAST_EXPECT(i32.value() == maxInt32); BEAST_EXPECT(i32.getText() == "2147483647"); BEAST_EXPECT(i32.getSType() == STI_INT32); diff --git a/src/test/protocol/STIssue_test.cpp b/src/test/protocol/STIssue_test.cpp index 7e6fe5487c..2ffdb1d9af 100644 --- a/src/test/protocol/STIssue_test.cpp +++ b/src/test/protocol/STIssue_test.cpp @@ -22,7 +22,7 @@ public: { issue = xrpIssue(); issue.account = alice; - STIssue stissue(sfAsset, Asset{issue}); + STIssue const stissue(sfAsset, Asset{issue}); fail("Inconsistent XRP Issue doesn't fail"); } catch (...) @@ -34,7 +34,7 @@ public: { issue = USD; issue.account = xrpAccount(); - STIssue stissue(sfAsset, Asset{issue}); + STIssue const stissue(sfAsset, Asset{issue}); fail("Inconsistent IOU Issue doesn't fail"); } catch (...) @@ -51,7 +51,7 @@ public: base_uint<320> uint; (void)uint.parseHex(data); SerialIter iter(Slice(uint.data(), uint.size())); - STIssue stissue(iter, sfAsset); + STIssue const stissue(iter, sfAsset); fail("Inconsistent IOU Issue doesn't fail on serializer"); } catch (...) @@ -61,7 +61,7 @@ public: try { - STIssue stissue(sfAsset, Asset{xrpIssue()}); + STIssue const stissue(sfAsset, Asset{xrpIssue()}); } catch (...) { @@ -70,7 +70,7 @@ public: try { - STIssue stissue(sfAsset, Asset{USD}); + STIssue const stissue(sfAsset, Asset{USD}); } catch (...) { @@ -85,7 +85,7 @@ public: base_uint<320> uint; (void)uint.parseHex(data); SerialIter iter(Slice(uint.data(), uint.size())); - STIssue stissue(iter, sfAsset); + STIssue const stissue(iter, sfAsset); BEAST_EXPECT(stissue.value() == USD); } catch (...) @@ -99,7 +99,7 @@ public: base_uint<160> uint; (void)uint.parseHex(data); SerialIter iter(Slice(uint.data(), uint.size())); - STIssue stissue(iter, sfAsset); + STIssue const stissue(iter, sfAsset); BEAST_EXPECT(stissue.value() == xrpCurrency()); } catch (...) diff --git a/src/test/protocol/STNumber_test.cpp b/src/test/protocol/STNumber_test.cpp index 9d43ace638..3b794b23b5 100644 --- a/src/test/protocol/STNumber_test.cpp +++ b/src/test/protocol/STNumber_test.cpp @@ -45,19 +45,19 @@ struct STNumber_test : public beast::unit_test::suite 0, 1, std::numeric_limits::max()}; - for (std::int64_t mantissa : mantissas) + for (std::int64_t const mantissa : mantissas) testCombo(Number{mantissa}); std::initializer_list const exponents = { Number::minExponent, -1, 0, 1, Number::maxExponent - 1}; - for (std::int32_t exponent : exponents) + for (std::int32_t const exponent : exponents) testCombo(Number{123, exponent}); { STAmount const strikePrice{noIssue(), 100}; STNumber const factor{sfNumber, 100}; auto const iouValue = strikePrice.iou(); - IOUAmount totalValue{iouValue * factor}; + IOUAmount const totalValue{iouValue * factor}; STAmount const totalAmount{totalValue, strikePrice.issue()}; BEAST_EXPECT(totalAmount == Number{10'000}); } @@ -95,7 +95,7 @@ struct STNumber_test : public beast::unit_test::suite BEAST_EXPECT(numberFromJson(sfNumber, "-0.000e6") == STNumber(sfNumber, 0)); { - NumberRoundModeGuard mg(Number::towards_zero); + NumberRoundModeGuard const mg(Number::towards_zero); // maxint64 9,223,372,036,854,775,807 auto const maxInt = std::to_string(std::numeric_limits::max()); // minint64 -9,223,372,036,854,775,808 @@ -276,7 +276,7 @@ struct STNumber_test : public beast::unit_test::suite for (auto const scale : {MantissaRange::small, MantissaRange::large}) { - NumberMantissaScaleGuard sg(scale); + NumberMantissaScaleGuard const sg(scale); testcase << to_string(Number::getMantissaScale()); doRun(); } diff --git a/src/test/protocol/STObject_test.cpp b/src/test/protocol/STObject_test.cpp index 058df3ba82..135c577fb4 100644 --- a/src/test/protocol/STObject_test.cpp +++ b/src/test/protocol/STObject_test.cpp @@ -13,7 +13,8 @@ public: unexpected(sfGeneric.isUseful(), "sfGeneric must not be useful"); { // Try to put sfGeneric in an SOTemplate. - except([&]() { SOTemplate elements{{sfGeneric, soeREQUIRED}}; }); + except( + [&]() { SOTemplate const elements{{sfGeneric, soeREQUIRED}}; }); } unexpected(sfInvalid.isUseful(), "sfInvalid must not be useful"); @@ -31,12 +32,13 @@ public: } { // Try to put sfInvalid in an SOTemplate. - except([&]() { SOTemplate elements{{sfInvalid, soeREQUIRED}}; }); + except( + [&]() { SOTemplate const elements{{sfInvalid, soeREQUIRED}}; }); } { // Try to put the same SField into an SOTemplate twice. except([&]() { - SOTemplate elements{ + SOTemplate const elements{ {sfAccount, soeREQUIRED}, {sfAccount, soeREQUIRED}, }; @@ -59,7 +61,7 @@ public: }; STObject object1(elements, sfTestObject); - STObject object2(object1); + STObject const object2(object1); unexpected(object1.getSerializer() != object2.getSerializer(), "STObject error 1"); @@ -106,7 +108,7 @@ public: for (int i = 0; i < 1000; i++) { - Blob j(i, 2); + Blob const j(i, 2); object1.setFieldVL(sfTestVL, j); @@ -114,7 +116,7 @@ public: object1.add(s); SerialIter it(s.slice()); - STObject object3(elements, it, sfTestObject); + STObject const object3(elements, it, sfTestObject); unexpected(object1.getFieldVL(sfTestVL) != j, "STObject error"); @@ -134,7 +136,7 @@ public: object1.add(s); SerialIter it(s.slice()); - STObject object3(elements, it, sfTestObject); + STObject const object3(elements, it, sfTestObject); auto const& uints1 = object1.getFieldV256(sfTestV256); auto const& uints3 = object3.getFieldV256(sfTestV256); @@ -169,8 +171,8 @@ public: BEAST_EXPECT(st[sf1Outer] == 1); BEAST_EXPECT(st[sf2Outer] == 2); except([&]() { st[sf3Outer]; }); - BEAST_EXPECT(*st[~sf1Outer] == 1); - BEAST_EXPECT(*st[~sf2Outer] == 2); + BEAST_EXPECT(*st[~sf1Outer] == 1); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT(*st[~sf2Outer] == 2); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(st[~sf3Outer] == std::nullopt); BEAST_EXPECT(!!st[~sf1Outer]); BEAST_EXPECT(!!st[~sf2Outer]); @@ -200,9 +202,9 @@ public: BEAST_EXPECT(st[sf1Outer] == 1); BEAST_EXPECT(st[sf2Outer] == 2); BEAST_EXPECT(st[sf3Outer] == 0); - BEAST_EXPECT(*st[~sf1Outer] == 1); - BEAST_EXPECT(*st[~sf2Outer] == 2); - BEAST_EXPECT(*st[~sf3Outer] == 0); + BEAST_EXPECT(*st[~sf1Outer] == 1); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT(*st[~sf2Outer] == 2); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT(*st[~sf3Outer] == 0); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(!!st[~sf1Outer]); BEAST_EXPECT(!!st[~sf2Outer]); BEAST_EXPECT(!!st[~sf3Outer]); @@ -351,7 +353,7 @@ public: Buffer b(1); BEAST_EXPECT(!b.empty()); st[sf4] = std::move(b); - BEAST_EXPECT(b.empty()); + BEAST_EXPECT(b.empty()); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(Slice(st[sf4]).size() == 1); st[~sf4] = std::nullopt; BEAST_EXPECT(!~st[~sf4]); @@ -370,7 +372,7 @@ public: BEAST_EXPECT(!!~st[~sf5]); Buffer b(1); st[sf5] = std::move(b); - BEAST_EXPECT(b.empty()); + BEAST_EXPECT(b.empty()); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(Slice(st[sf5]).size() == 1); st[~sf4] = std::nullopt; BEAST_EXPECT(!~st[~sf4]); @@ -399,7 +401,7 @@ public: st[sf] = std::move(v); auto const& cst = st; BEAST_EXPECT(cst[sf].size() == 2); - BEAST_EXPECT(cst[~sf]->size() == 2); + BEAST_EXPECT(cst[~sf]->size() == 2); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(cst[sf][0] == 1); BEAST_EXPECT(cst[sf][1] == 2); static_assert( @@ -420,9 +422,9 @@ public: STObject st(sot, sfGeneric); auto const& cst(st); - BEAST_EXPECT(cst[sf1].size() == 0); + BEAST_EXPECT(cst[sf1].empty()); BEAST_EXPECT(!cst[~sf2]); - BEAST_EXPECT(cst[sf3].size() == 0); + BEAST_EXPECT(cst[sf3].empty()); std::vector v; v.emplace_back(1); st[sf1] = v; @@ -437,7 +439,7 @@ public: BEAST_EXPECT(cst[sf3].size() == 1); BEAST_EXPECT(cst[sf3][0] == uint256{1}); st[sf3] = std::vector{}; - BEAST_EXPECT(cst[sf3].size() == 0); + BEAST_EXPECT(cst[sf3].empty()); } } // namespace xrpl @@ -475,7 +477,7 @@ public: run() override { // Instantiate a jtx::Env so debugLog writes are exercised. - test::jtx::Env env(*this); + test::jtx::Env const env(*this); testFields(); testSerialization(); diff --git a/src/test/protocol/STParsedJSON_test.cpp b/src/test/protocol/STParsedJSON_test.cpp index 6838f557a9..1c86a90348 100644 --- a/src/test/protocol/STParsedJSON_test.cpp +++ b/src/test/protocol/STParsedJSON_test.cpp @@ -11,7 +11,7 @@ namespace xrpl { class STParsedJSON_test : public beast::unit_test::suite { - bool + static bool parseJSONString(std::string const& json, Json::Value& to) { Json::Reader reader; @@ -27,7 +27,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfCloseResolution] = 255; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfCloseResolution)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 255); } @@ -37,7 +39,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfCloseResolution] = 255u; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfCloseResolution)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 255); } @@ -47,7 +51,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfCloseResolution] = "255"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfCloseResolution)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 255); } @@ -57,6 +63,7 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfCloseResolution] = 0; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 0); } @@ -64,7 +71,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfCloseResolution] = -1; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -72,7 +79,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfCloseResolution] = 256; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -80,7 +87,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfCloseResolution] = Json::Value(Json::arrayValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -88,7 +95,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfCloseResolution] = Json::Value(Json::objectValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -103,7 +110,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLedgerEntryType] = 65535; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerEntryType)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 65535); } @@ -113,7 +122,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLedgerEntryType] = 65535u; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerEntryType)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 65535); } @@ -123,7 +134,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLedgerEntryType] = "65535"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerEntryType)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 65535); } @@ -133,6 +146,7 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLedgerEntryType] = 0; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 0); } @@ -140,7 +154,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfLedgerEntryType] = -1; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -148,7 +162,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfLedgerEntryType] = 65536; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -156,7 +170,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfLedgerEntryType] = "65536"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -164,7 +178,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfLedgerEntryType] = Json::Value(Json::arrayValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -172,7 +186,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfLedgerEntryType] = Json::Value(Json::objectValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -180,7 +194,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfTransferFee] = "Payment"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -194,7 +208,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNetworkID] = 4294967295u; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfNetworkID)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU32(sfNetworkID) == 4294967295u); } @@ -204,7 +220,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNetworkID] = "4294967295"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfNetworkID)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU32(sfNetworkID) == 4294967295u); } @@ -214,6 +232,7 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNetworkID] = 0; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU32(sfNetworkID) == 0); } @@ -221,7 +240,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfNetworkID] = -1; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -229,7 +248,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfNetworkID] = "4294967296"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -237,7 +256,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfNetworkID] = Json::Value(Json::arrayValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -245,7 +264,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfNetworkID] = Json::Value(Json::objectValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -259,7 +278,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfIndexNext] = "ffffffffffffffff"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfIndexNext)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU64(sfIndexNext) == 18446744073709551615ull); } @@ -269,6 +290,7 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfIndexNext] = 0; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU64(sfIndexNext) == 0ull); } @@ -276,7 +298,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfIndexNext] = -1; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -287,7 +309,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfIndexNext] = "10000000000000000"; // uint64 max + 1 (in hex) - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -295,7 +317,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfIndexNext] = "0xabcdefabcdef"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -303,7 +325,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfIndexNext] = "abcdefga"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -311,7 +333,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfIndexNext] = Json::Value(Json::arrayValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -319,7 +341,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfIndexNext] = Json::Value(Json::objectValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -333,9 +355,11 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfEmailHash] = "0123456789ABCDEF0123456789ABCDEF"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash).size() == 16); - std::array expected = { + std::array const expected = { 0x01, 0x23, 0x45, @@ -352,6 +376,7 @@ class STParsedJSON_test : public beast::unit_test::suite 0xAB, 0xCD, 0xEF}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash) == uint128{expected}); } @@ -361,7 +386,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfEmailHash] = "0123456789abcdef0123456789abcdef"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash).size() == 16); } @@ -371,10 +398,13 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfEmailHash] = ""; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& h128 = obj.object->getFieldH128(sfEmailHash); BEAST_EXPECT(h128.size() == 16); - bool allZero = std::all_of(h128.begin(), h128.end(), [](auto b) { return b == 0; }); + bool const allZero = + std::all_of(h128.begin(), h128.end(), [](auto b) { return b == 0; }); BEAST_EXPECT(allZero); } @@ -382,7 +412,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfEmailHash] = "0123456789ABCDEF0123456789ABCDE"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -390,7 +420,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfEmailHash] = "nothexstring"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -398,7 +428,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfEmailHash] = "01234567"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -406,7 +436,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfEmailHash] = "0123456789ABCDEF0123456789ABCDEF00"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -414,7 +444,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfEmailHash] = Json::Value(Json::arrayValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -422,7 +452,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfEmailHash] = Json::Value(Json::objectValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -436,11 +466,14 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfTakerPaysCurrency] = "0123456789ABCDEF0123456789ABCDEF01234567"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH160(sfTakerPaysCurrency).size() == 20); - std::array expected = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, - 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, - 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67}; + std::array const expected = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, + 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, + 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH160(sfTakerPaysCurrency) == uint160{expected}); } // Valid lowercase hex string for UInt160 @@ -449,7 +482,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfTakerPaysCurrency] = "0123456789abcdef0123456789abcdef01234567"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH160(sfTakerPaysCurrency).size() == 20); } @@ -459,10 +494,13 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfTakerPaysCurrency] = ""; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& h160 = obj.object->getFieldH160(sfTakerPaysCurrency); BEAST_EXPECT(h160.size() == 20); - bool allZero = std::all_of(h160.begin(), h160.end(), [](auto b) { return b == 0; }); + bool const allZero = + std::all_of(h160.begin(), h160.end(), [](auto b) { return b == 0; }); BEAST_EXPECT(allZero); } @@ -470,7 +508,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfTakerPaysCurrency] = "nothexstring"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -478,7 +516,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfTakerPaysCurrency] = "01234567"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -486,7 +524,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfTakerPaysCurrency] = "0123456789ABCDEF0123456789ABCDEF0123456789"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -494,7 +532,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfTakerPaysCurrency] = Json::Value(Json::arrayValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -502,7 +540,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfTakerPaysCurrency] = Json::Value(Json::objectValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -516,11 +554,14 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfMPTokenIssuanceID] = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfMPTokenIssuanceID)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH192(sfMPTokenIssuanceID).size() == 24); - std::array expected = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + std::array const expected = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH192(sfMPTokenIssuanceID) == uint192{expected}); } @@ -530,7 +571,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfMPTokenIssuanceID] = "ffffffffffffffffffffffffffffffffffffffffffffffff"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfMPTokenIssuanceID)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH192(sfMPTokenIssuanceID).size() == 24); } @@ -540,10 +583,13 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfMPTokenIssuanceID] = ""; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfMPTokenIssuanceID)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& h192 = obj.object->getFieldH192(sfMPTokenIssuanceID); BEAST_EXPECT(h192.size() == 24); - bool allZero = std::all_of(h192.begin(), h192.end(), [](auto b) { return b == 0; }); + bool const allZero = + std::all_of(h192.begin(), h192.end(), [](auto b) { return b == 0; }); BEAST_EXPECT(allZero); } @@ -551,7 +597,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfMPTokenIssuanceID] = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDE"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -559,7 +605,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfMPTokenIssuanceID] = "nothexstring"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -567,7 +613,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfMPTokenIssuanceID] = "01234567"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -575,7 +621,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfMPTokenIssuanceID] = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF00"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -583,7 +629,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfMPTokenIssuanceID] = Json::Value(Json::arrayValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -591,7 +637,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfMPTokenIssuanceID] = Json::Value(Json::objectValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -608,12 +654,15 @@ class STParsedJSON_test : public beast::unit_test::suite "EF"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash).size() == 32); - std::array expected = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, - 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, - 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, - 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; + std::array const expected = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, + 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, + 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash) == uint256{expected}); } // Valid lowercase hex string for UInt256 @@ -624,7 +673,9 @@ class STParsedJSON_test : public beast::unit_test::suite "ef"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash).size() == 32); } @@ -634,10 +685,13 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLedgerHash] = ""; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& h256 = obj.object->getFieldH256(sfLedgerHash); BEAST_EXPECT(h256.size() == 32); - bool allZero = std::all_of(h256.begin(), h256.end(), [](auto b) { return b == 0; }); + bool const allZero = + std::all_of(h256.begin(), h256.end(), [](auto b) { return b == 0; }); BEAST_EXPECT(allZero); } @@ -647,7 +701,7 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLedgerHash] = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCD" "E"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -655,7 +709,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfLedgerHash] = "nothexstring"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -663,7 +717,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfLedgerHash] = "01234567"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -673,7 +727,7 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLedgerHash] = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCD" "EF00"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -681,7 +735,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfLedgerHash] = Json::Value(Json::arrayValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -689,7 +743,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfLedgerHash] = Json::Value(Json::objectValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -704,8 +758,12 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLoanScale] = minInt32; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale))) + { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == minInt32); + } } // max value @@ -715,8 +773,12 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLoanScale] = maxInt32; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale))) + { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == maxInt32); + } } // max uint value @@ -726,9 +788,13 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLoanScale] = maxUInt32; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale))) + { BEAST_EXPECT( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) obj.object->getFieldI32(sfLoanScale) == static_cast(maxUInt32)); + } } // Test with string value @@ -737,26 +803,34 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLoanScale] = "2147483647"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale))) + { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == 2147483647u); + } } // Test with string negative value { Json::Value j; - int value = -2147483648; + int const value = -2147483648; j[sfLoanScale] = std::to_string(value); STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale))) + { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == value); + } } // Test out of range value for int32 (negative) { Json::Value j; j[sfLoanScale] = "-2147483649"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -764,7 +838,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfLoanScale] = 2147483648u; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -772,7 +846,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfLoanScale] = "2147483648"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -780,7 +854,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfLoanScale] = Json::Value(Json::arrayValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -788,7 +862,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfLoanScale] = Json::Value(Json::objectValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -803,8 +877,10 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfPublicKey] = "DEADBEEF"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfPublicKey)); - auto const& blob = obj.object->getFieldVL(sfPublicKey); + auto const& blob = + obj.object->getFieldVL(sfPublicKey); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(blob.size() == 4); BEAST_EXPECT(blob[0] == 0xDE); BEAST_EXPECT(blob[1] == 0xAD); @@ -818,9 +894,11 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfPublicKey] = ""; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfPublicKey)); - auto const& blob = obj.object->getFieldVL(sfPublicKey); - BEAST_EXPECT(blob.size() == 0); + auto const& blob = + obj.object->getFieldVL(sfPublicKey); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT(blob.empty()); } // Test lowercase hex string for blob @@ -829,8 +907,10 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfPublicKey] = "deadbeef"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfPublicKey)); - auto const& blob = obj.object->getFieldVL(sfPublicKey); + auto const& blob = + obj.object->getFieldVL(sfPublicKey); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(blob.size() == 4); BEAST_EXPECT(blob[0] == 0xDE); BEAST_EXPECT(blob[1] == 0xAD); @@ -842,7 +922,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfPublicKey] = "XYZ123"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -850,7 +930,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfPublicKey] = Json::Value(Json::arrayValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -858,7 +938,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfPublicKey] = Json::Value(Json::objectValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -880,8 +960,10 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfHashes] = arr; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfHashes)); - auto const& vec = obj.object->getFieldV256(sfHashes); + auto const& vec = + obj.object->getFieldV256(sfHashes); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(vec.size() == 2); BEAST_EXPECT(to_string(vec[0]) == arr[0u].asString()); BEAST_EXPECT(to_string(vec[1]) == arr[1u].asString()); @@ -889,13 +971,15 @@ class STParsedJSON_test : public beast::unit_test::suite // Test empty array for Vector256 (should be valid, size 0) { Json::Value j; - Json::Value arr(Json::arrayValue); + Json::Value const arr(Json::arrayValue); j[sfHashes] = arr; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfHashes)); - auto const& vec = obj.object->getFieldV256(sfHashes); - BEAST_EXPECT(vec.size() == 0); + auto const& vec = + obj.object->getFieldV256(sfHashes); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT(vec.empty()); } // Test array with invalid hex string (should fail) @@ -904,7 +988,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value arr(Json::arrayValue); arr.append("nothexstring"); j[sfHashes] = arr; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -914,7 +998,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value arr(Json::arrayValue); arr.append("0123456789ABCDEF"); // too short for uint256 j[sfHashes] = arr; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -924,7 +1008,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value arr(Json::arrayValue); arr.append(12345); j[sfHashes] = arr; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -932,7 +1016,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfHashes] = "notanarray"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -944,7 +1028,7 @@ class STParsedJSON_test : public beast::unit_test::suite objElem["foo"] = "bar"; arr.append(objElem); j[sfHashes] = arr; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -959,8 +1043,10 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfAccount] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfAccount)); - auto const& acct = obj.object->getAccountID(sfAccount); + auto const& acct = + obj.object->getAccountID(sfAccount); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(acct.size() == 20); BEAST_EXPECT(toBase58(acct) == "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"); } @@ -971,8 +1057,10 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfAccount] = "000102030405060708090A0B0C0D0E0F10111213"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfAccount)); - auto const& acct = obj.object->getAccountID(sfAccount); + auto const& acct = + obj.object->getAccountID(sfAccount); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(acct.size() == 20); } @@ -980,7 +1068,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfAccount] = "notAValidBase58Account"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -988,7 +1076,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfAccount] = "001122334455"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -996,7 +1084,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfAccount] = "000102030405060708090A0B0C0D0E0F101112131415"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1004,7 +1092,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfAccount] = "000102030405060708090A0B0C0D0E0F1011121G"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1012,7 +1100,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfAccount] = ""; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1020,7 +1108,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfAccount] = Json::Value(Json::arrayValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1028,7 +1116,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfAccount] = Json::Value(Json::objectValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -1043,7 +1131,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfBaseAsset] = "USD"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& curr = obj.object->getFieldCurrency(sfBaseAsset); BEAST_EXPECT(curr.currency().size() == 20); } @@ -1054,7 +1144,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfBaseAsset] = "EUR"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& curr = obj.object->getFieldCurrency(sfBaseAsset); BEAST_EXPECT(curr.currency().size() == 20); } @@ -1064,7 +1156,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value j; j[sfBaseAsset] = "0123456789ABCDEF01230123456789ABCDEF0123"; STParsedJSONObject obj("Test", j); - if (BEAST_EXPECT(obj.object.has_value())) + if (BEAST_EXPECT(obj.object); obj.object.has_value()) { BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset)); auto const& curr = obj.object->getFieldCurrency(sfBaseAsset); @@ -1076,7 +1168,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfBaseAsset] = "USDD"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1086,7 +1178,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfBaseAsset] = "usd"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& curr = obj.object->getFieldCurrency(sfBaseAsset); BEAST_EXPECT(curr.currency().size() == 20); } @@ -1095,7 +1189,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfBaseAsset] = "0123456789AB"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1103,7 +1197,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfBaseAsset] = "0123456789ABCDEF0123456789"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1113,7 +1207,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfBaseAsset] = ""; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& curr = obj.object->getFieldCurrency(sfBaseAsset); BEAST_EXPECT(curr.currency().size() == 20); } @@ -1122,7 +1218,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfBaseAsset] = Json::Value(Json::arrayValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1130,7 +1226,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfBaseAsset] = Json::Value(Json::objectValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -1145,7 +1241,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfAmount] = "100000000000000000"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfAmount)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldAmount(sfAmount) == STAmount(100000000000000000ull)); } @@ -1155,7 +1253,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfAmount] = 4294967295u; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfAmount)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldAmount(sfAmount) == STAmount(4294967295u)); } @@ -1163,7 +1263,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfAmount] = "123.45"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1171,7 +1271,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfAmount] = ""; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1179,7 +1279,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfAmount] = "notanumber"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1187,7 +1287,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfAmount] = Json::Value(Json::objectValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -1211,7 +1311,9 @@ class STParsedJSON_test : public beast::unit_test::suite STParsedJSONObject obj("Test", j); if (BEAST_EXPECT(obj.object.has_value())) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfPaths)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& ps = obj.object->getFieldPathSet(sfPaths); BEAST_EXPECT(!ps.empty()); BEAST_EXPECT(ps.size() == 1); @@ -1240,8 +1342,10 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfPaths] = pathset; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); - BEAST_EXPECT(obj.object->isFieldPresent(sfPaths)); - auto const& ps = obj.object->getFieldPathSet(sfPaths); + BEAST_EXPECT( + obj.object->isFieldPresent(sfPaths)); // NOLINT(bugprone-unchecked-optional-access) + auto const& ps = + obj.object->getFieldPathSet(sfPaths); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(!ps.empty()); } @@ -1249,7 +1353,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfPaths] = "notanarray"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1259,7 +1363,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value pathset(Json::arrayValue); pathset.append("notanarray"); j[sfPaths] = pathset; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1272,7 +1376,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value pathset(Json::arrayValue); pathset.append(path); j[sfPaths] = pathset; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1287,7 +1391,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value pathset(Json::arrayValue); pathset.append(path); j[sfPaths] = pathset; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1302,7 +1406,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value pathset(Json::arrayValue); pathset.append(path); j[sfPaths] = pathset; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1316,7 +1420,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value pathset(Json::arrayValue); pathset.append(path); j[sfPaths] = pathset; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1330,7 +1434,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value pathset(Json::arrayValue); pathset.append(path); j[sfPaths] = pathset; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1344,7 +1448,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value pathset(Json::arrayValue); pathset.append(path); j[sfPaths] = pathset; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1358,7 +1462,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value pathset(Json::arrayValue); pathset.append(path); j[sfPaths] = pathset; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1372,7 +1476,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value pathset(Json::arrayValue); pathset.append(path); j[sfPaths] = pathset; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -1391,8 +1495,10 @@ class STParsedJSON_test : public beast::unit_test::suite STParsedJSONObject obj("Test", j); if (BEAST_EXPECT(obj.object.has_value())) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfAsset)); - auto const& issueField = (*obj.object)[sfAsset]; + auto const& issueField = + (*obj.object)[sfAsset]; // NOLINT(bugprone-unchecked-optional-access) auto const issue = issueField.value().get(); BEAST_EXPECT(issue.currency.size() == 20); BEAST_EXPECT(to_string(issue.currency) == "USD"); @@ -1410,7 +1516,7 @@ class STParsedJSON_test : public beast::unit_test::suite issueJson["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; j[sfAsset] = issueJson; STParsedJSONObject obj("Test", j); - if (BEAST_EXPECT(obj.object.has_value())) + if (BEAST_EXPECT(obj.object); obj.object.has_value()) { BEAST_EXPECT(obj.object->isFieldPresent(sfAsset)); auto const& issueField = (*obj.object)[sfAsset]; @@ -1427,7 +1533,7 @@ class STParsedJSON_test : public beast::unit_test::suite issueJson["mpt_issuance_id"] = "0000000000000000000000004D5054494431323334234234"; j[sfAsset] = issueJson; STParsedJSONObject obj("Test", j); - if (BEAST_EXPECT(obj.object.has_value())) + if (BEAST_EXPECT(obj.object); obj.object.has_value()) { BEAST_EXPECT(obj.object->isFieldPresent(sfAsset)); auto const& issueField = (*obj.object)[sfAsset]; @@ -1442,7 +1548,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value issue(Json::objectValue); issue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; j[sfAsset] = issue; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1452,7 +1558,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value issue(Json::objectValue); issue["currency"] = "USD"; j[sfAsset] = issue; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1463,7 +1569,7 @@ class STParsedJSON_test : public beast::unit_test::suite issue["currency"] = "USDD"; issue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; j[sfAsset] = issue; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1474,7 +1580,7 @@ class STParsedJSON_test : public beast::unit_test::suite issue["currency"] = "USD"; issue["issuer"] = "notAValidIssuer"; j[sfAsset] = issue; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1485,7 +1591,7 @@ class STParsedJSON_test : public beast::unit_test::suite issue["currency"] = Json::Value(Json::arrayValue); issue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; j[sfAsset] = issue; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1496,7 +1602,7 @@ class STParsedJSON_test : public beast::unit_test::suite issue["currency"] = "USD"; issue["issuer"] = Json::Value(Json::objectValue); j[sfAsset] = issue; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1504,7 +1610,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfAsset] = "notanobject"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -1529,7 +1635,7 @@ class STParsedJSON_test : public beast::unit_test::suite bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; j[sfXChainBridge] = bridge; STParsedJSONObject obj("Test", j); - if (BEAST_EXPECT(obj.object.has_value())) + if (BEAST_EXPECT(obj.object); obj.object.has_value()) { BEAST_EXPECT(obj.object->isFieldPresent(sfXChainBridge)); auto const& bridgeField = (*obj.object)[sfXChainBridge]; @@ -1554,7 +1660,7 @@ class STParsedJSON_test : public beast::unit_test::suite bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; j[sfXChainBridge] = bridge; STParsedJSONObject obj("Test", j); - if (BEAST_EXPECT(obj.object.has_value())) + if (BEAST_EXPECT(obj.object); obj.object.has_value()) { BEAST_EXPECT(obj.object->isFieldPresent(sfXChainBridge)); auto const& bridgeField = (*obj.object)[sfXChainBridge]; @@ -1574,7 +1680,7 @@ class STParsedJSON_test : public beast::unit_test::suite bridge["LockingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; j[sfXChainBridge] = bridge; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1589,7 +1695,7 @@ class STParsedJSON_test : public beast::unit_test::suite bridge["LockingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; j[sfXChainBridge] = bridge; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1607,7 +1713,7 @@ class STParsedJSON_test : public beast::unit_test::suite bridge["LockingChainIssue"] = lockingChainIssue; bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; j[sfXChainBridge] = bridge; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1625,7 +1731,7 @@ class STParsedJSON_test : public beast::unit_test::suite bridge["LockingChainIssue"] = lockingChainIssue; bridge["LockingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; j[sfXChainBridge] = bridge; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1636,7 +1742,7 @@ class STParsedJSON_test : public beast::unit_test::suite bridge["LockingChainIssue"] = "notanobject"; bridge["IssuingChainIssue"] = "notanobject"; j[sfXChainBridge] = bridge; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1652,7 +1758,7 @@ class STParsedJSON_test : public beast::unit_test::suite bridge["LockingChainIssue"] = lockingChainIssue; bridge["IssuingChainIssue"] = asset; j[sfXChainBridge] = bridge; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1668,7 +1774,7 @@ class STParsedJSON_test : public beast::unit_test::suite bridge["LockingChainIssue"] = lockingChainIssue; bridge["IssuingChainIssue"] = asset; j[sfXChainBridge] = bridge; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1685,7 +1791,7 @@ class STParsedJSON_test : public beast::unit_test::suite bridge["LockingChainIssue"] = lockingChainIssue; bridge["IssuingChainIssue"] = asset; j[sfXChainBridge] = bridge; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1693,7 +1799,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfXChainBridge] = "notanobject"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -1708,7 +1814,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNumber] = 12345; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfNumber)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(12345, 0)); } @@ -1718,7 +1826,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNumber] = 12345u; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfNumber)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(12345, 0)); } @@ -1728,7 +1838,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNumber] = "67890"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfNumber)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(67890, 0)); } @@ -1738,7 +1850,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNumber] = -42; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfNumber)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(-42, 0)); } @@ -1748,7 +1862,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNumber] = "-123"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfNumber)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(-123, 0)); } @@ -1757,7 +1873,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value j; j[sfNumber] = "3.14159"; STParsedJSONObject obj("Test", j); - if (BEAST_EXPECT(obj.object.has_value())) + if (BEAST_EXPECT(obj.object); obj.object.has_value()) { BEAST_EXPECT(obj.object->isFieldPresent(sfNumber)); BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(314159, -5)); @@ -1768,7 +1884,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfNumber] = "notanumber"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1776,7 +1892,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfNumber] = Json::Value(Json::arrayValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1784,7 +1900,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfNumber] = Json::Value(Json::objectValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1792,7 +1908,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfNumber] = ""; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } } @@ -1809,7 +1925,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfTransactionMetaData] = objVal; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfTransactionMetaData)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& result = obj.object->peekFieldObject(sfTransactionMetaData); BEAST_EXPECT(result.getFieldU8(sfTransactionResult) == 1); } @@ -1818,7 +1936,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfTransactionMetaData] = "notanobject"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1828,7 +1946,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value arr(Json::arrayValue); arr.append(1); j[sfTransactionMetaData] = arr; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1836,7 +1954,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfTransactionMetaData] = Json::Value(Json::nullValue); - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1848,7 +1966,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value* current = &obj; for (int i = 0; i < 63; ++i) { - Json::Value next(Json::objectValue); + Json::Value const next(Json::objectValue); (*current)[sfTransactionMetaData] = next; current = &((*current)[sfTransactionMetaData]); } @@ -1856,6 +1974,7 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfTransactionMetaData] = obj; STParsedJSONObject parsed("Test", j); BEAST_EXPECT(parsed.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(parsed.object->isFieldPresent(sfTransactionMetaData)); } @@ -1866,13 +1985,13 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value* current = &obj; for (int i = 0; i < 64; ++i) { - Json::Value next(Json::objectValue); + Json::Value const next(Json::objectValue); (*current)[sfTransactionMetaData] = next; current = &((*current)[sfTransactionMetaData]); } (*current)[sfTransactionResult.getJsonName()] = 1; j[sfTransactionMetaData] = obj; - STParsedJSONObject parsed("Test", j); + STParsedJSONObject const parsed("Test", j); BEAST_EXPECT(!parsed.object.has_value()); } } @@ -1893,7 +2012,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfSignerEntries] = arr; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfSignerEntries)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& result = obj.object->getFieldArray(sfSignerEntries); if (BEAST_EXPECT(result.size() == 1)) { @@ -1908,7 +2029,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value arr(Json::arrayValue); arr.append("notanobject"); j[sfSignerEntries] = arr; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1920,7 +2041,7 @@ class STParsedJSON_test : public beast::unit_test::suite elem["invalidField"] = 1; arr.append(elem); j[sfSignerEntries] = arr; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1933,7 +2054,7 @@ class STParsedJSON_test : public beast::unit_test::suite elem[sfNetworkID] = 3; arr.append(elem); j[sfSignerEntries] = arr; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1941,7 +2062,7 @@ class STParsedJSON_test : public beast::unit_test::suite { Json::Value j; j[sfSignerEntries] = "notanarray"; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } @@ -1954,17 +2075,18 @@ class STParsedJSON_test : public beast::unit_test::suite elem[sfTransactionResult] = "notanint"; arr.append(elem); j[sfSignerEntries] = arr; - STParsedJSONObject obj("Test", j); + STParsedJSONObject const obj("Test", j); BEAST_EXPECT(!obj.object.has_value()); } // Test with empty array for Array (should be valid) { Json::Value j; - Json::Value arr(Json::arrayValue); + Json::Value const arr(Json::arrayValue); j[sfSignerEntries] = arr; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfSignerEntries)); } @@ -1975,7 +2097,7 @@ class STParsedJSON_test : public beast::unit_test::suite obj.append(Json::Value(Json::objectValue)); obj[0u][sfTransactionResult] = 1; j[sfSignerEntries] = obj; - STParsedJSONObject parsed("Test", j); + STParsedJSONObject const parsed("Test", j); BEAST_EXPECT(!parsed.object.has_value()); } @@ -1995,22 +2117,22 @@ class STParsedJSON_test : public beast::unit_test::suite them. */ - std::string faulty( + std::string const faulty( "{\"Template\":[{" "\"ModifiedNode\":{\"Sequence\":1}, " "\"DeletedNode\":{\"Sequence\":1}" "}]}"); - std::unique_ptr so; + std::unique_ptr const so; Json::Value faultyJson; - bool parsedOK(parseJSONString(faulty, faultyJson)); + bool const parsedOK(parseJSONString(faulty, faultyJson)); unexpected(!parsedOK, "failed to parse"); - STParsedJSONObject parsed("test", faultyJson); + STParsedJSONObject const parsed("test", faultyJson); BEAST_EXPECT(!parsed.object); } catch (std::runtime_error& e) { - std::string what(e.what()); + std::string const what(e.what()); unexpected(what.find("First level children of `Template`") != 0); } } @@ -2038,6 +2160,7 @@ class STParsedJSON_test : public beast::unit_test::suite if (BEAST_EXPECT(parsed.object)) { std::string const& serialized( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) to_string(parsed.object->getJson(JsonOptions::none))); BEAST_EXPECT(serialized == goodJson); } @@ -2062,6 +2185,7 @@ class STParsedJSON_test : public beast::unit_test::suite if (BEAST_EXPECT(parsed.object)) { std::string const& serialized( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) to_string(parsed.object->getJson(JsonOptions::none))); BEAST_EXPECT(serialized == expectedJson); } @@ -2086,6 +2210,7 @@ class STParsedJSON_test : public beast::unit_test::suite if (BEAST_EXPECT(parsed.object)) { std::string const& serialized( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) to_string(parsed.object->getJson(JsonOptions::none))); BEAST_EXPECT(serialized == expectedJson); } @@ -2194,6 +2319,7 @@ class STParsedJSON_test : public beast::unit_test::suite if (BEAST_EXPECT(parsed.object)) { std::string const& serialized( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) to_string(parsed.object->getJson(JsonOptions::none))); BEAST_EXPECT(serialized == expectedJson); } @@ -2258,7 +2384,7 @@ class STParsedJSON_test : public beast::unit_test::suite run() override { // Instantiate a jtx::Env so debugLog writes are exercised. - test::jtx::Env env(*this); + test::jtx::Env const env(*this); testUInt8(); testUInt16(); testUInt32(); diff --git a/src/test/protocol/STTx_test.cpp b/src/test/protocol/STTx_test.cpp index 86e5e88d0a..0804c89bd4 100644 --- a/src/test/protocol/STTx_test.cpp +++ b/src/test/protocol/STTx_test.cpp @@ -1149,7 +1149,7 @@ public: // Construct an SOTemplate to get the ball rolling on building // an STObject that can contain an STArray. - SOTemplate recurse{ + SOTemplate const recurse{ {sfTransactionMetaData, soeOPTIONAL}, {sfTransactionHash, soeOPTIONAL}, {sfTemplate, soeOPTIONAL}, @@ -1211,7 +1211,7 @@ public: // Make an otherwise legit STTx with a duplicate field. Should // generate an exception when we deserialize. auto const keypair = randomKeyPair(KeyType::secp256k1); - STTx acctSet(ttACCOUNT_SET, [&keypair](auto& obj) { + STTx const acctSet(ttACCOUNT_SET, [&keypair](auto& obj) { obj.setAccountID(sfAccount, calcAccountID(keypair.first)); obj.setFieldU32(sfSequence, 7); obj.setFieldAmount(sfFee, STAmount(2557891634ull)); @@ -1329,7 +1329,7 @@ public: Serializer rawTxn; j.add(rawTxn); SerialIter sit(rawTxn.slice()); - STTx copy(sit); + STTx const copy(sit); if (copy != j) { @@ -1343,10 +1343,11 @@ public: } STParsedJSONObject parsed("test", j.getJson(JsonOptions::none)); - if (!parsed.object) + if (!parsed.object.has_value()) + { fail("Unable to build object from json"); - - if (STObject(j) != parsed.object) + } + else if (STObject(j) != parsed.object) { log << "ORIG: " << j.getJson(JsonOptions::none) << '\n' << "BUILT " << parsed.object->getJson(JsonOptions::none) << std::endl; @@ -1465,7 +1466,7 @@ public: auto const id2 = calcAccountID(kp2.first); // Get the stream of the transaction for use in multi-signing. - Serializer s = buildMultiSigningData(txn, id2); + Serializer const s = buildMultiSigningData(txn, id2); auto const saMultiSignature = sign(kp2.first, kp2.second, s.slice()); @@ -1474,7 +1475,7 @@ public: // Signer { // Account: "...", // TxnSignature: "...", - // PublicKey: "..."" + // PublicKey: "..." // } // Make one well formed Signer and several mal-formed ones. See // whether the serializer lets the good one through and catches @@ -1496,7 +1497,7 @@ public: bool serialized = false; try { - STTx copy(sit); + STTx const copy(sit); serialized = true; } catch (std::exception const&) diff --git a/src/test/protocol/SecretKey_test.cpp b/src/test/protocol/SecretKey_test.cpp index de21c7d88b..9072f5c0d9 100644 --- a/src/test/protocol/SecretKey_test.cpp +++ b/src/test/protocol/SecretKey_test.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -70,12 +72,16 @@ public: { auto const canonicality = ecdsaCanonicality(makeSlice(sig)); BEAST_EXPECT(canonicality); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*canonicality == ECDSACanonicality::fullyCanonical); } { auto const canonicality = ecdsaCanonicality(makeSlice(non)); BEAST_EXPECT(canonicality); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*canonicality != ECDSACanonicality::fullyCanonical); } @@ -95,6 +101,8 @@ public: auto const [pk, sk] = randomKeyPair(KeyType::secp256k1); BEAST_EXPECT(pk == derivePublicKey(KeyType::secp256k1, sk)); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*publicKeyType(pk) == KeyType::secp256k1); for (std::size_t j = 0; j < 32; j++) @@ -104,7 +112,7 @@ public: auto sig = signDigest(pk, sk, digest); - BEAST_EXPECT(sig.size() != 0); + BEAST_EXPECT(!sig.empty()); BEAST_EXPECT(verifyDigest(pk, digest, sig, true)); // Wrong digest: @@ -133,7 +141,7 @@ public: auto const [pk, sk] = randomKeyPair(type); BEAST_EXPECT(pk == derivePublicKey(type, sk)); - BEAST_EXPECT(*publicKeyType(pk) == type); + BEAST_EXPECT(*publicKeyType(pk) == type); // NOLINT(bugprone-unchecked-optional-access) for (std::size_t j = 0; j < 32; j++) { @@ -142,7 +150,7 @@ public: auto sig = sign(pk, sk, makeSlice(data)); - BEAST_EXPECT(sig.size() != 0); + BEAST_EXPECT(!sig.empty()); BEAST_EXPECT(verify(pk, makeSlice(data), sig)); // Construct wrong data: @@ -183,7 +191,7 @@ public: TokenType::NodePrivate, "pnen77YEeUd4fFKG7iycBWcwKpTaeFRkW2WFostaATy1DSupwXe"); BEAST_EXPECT(sk2); - BEAST_EXPECT(sk1 == *sk2); + BEAST_EXPECT(test::equal(sk1, *sk2)); // NOLINT(bugprone-unchecked-optional-access) } { @@ -193,7 +201,7 @@ public: TokenType::NodePrivate, "paKv46LztLqK3GaKz1rG2nQGN6M4JLyRtxFBYFTw4wAVHtGys36"); BEAST_EXPECT(sk2); - BEAST_EXPECT(sk1 == *sk2); + BEAST_EXPECT(test::equal(sk1, *sk2)); // NOLINT(bugprone-unchecked-optional-access) } // Try converting short, long and malformed data @@ -208,7 +216,7 @@ public: auto s = good; // Remove all characters from the string in random order: - std::hash r; + std::hash const r; while (!s.empty()) { @@ -261,20 +269,21 @@ public: BEAST_EXPECT(!si.empty()); auto const ski = parseBase58(TokenType::NodePrivate, si); - BEAST_EXPECT(ski && keys[i] == *ski); + BEAST_EXPECT(ski && test::equal(keys[i], *ski)); for (std::size_t j = i; j != keys.size(); ++j) { - BEAST_EXPECT((keys[i] == keys[j]) == (i == j)); + BEAST_EXPECT(test::equal(keys[i], keys[j]) == (i == j)); auto const sj = toBase58(TokenType::NodePrivate, keys[j]); BEAST_EXPECT((si == sj) == (i == j)); auto const skj = parseBase58(TokenType::NodePrivate, sj); - BEAST_EXPECT(skj && keys[j] == *skj); + BEAST_EXPECT(skj && test::equal(keys[j], *skj)); - BEAST_EXPECT((*ski == *skj) == (i == j)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + BEAST_EXPECT(test::equal(*ski, *skj) == (i == j)); } } } @@ -292,8 +301,9 @@ public: auto kp = generateKeyPair(KeyType::secp256k1, Seed{makeSlice(test.seed)}); BEAST_EXPECT(kp.first == PublicKey{makeSlice(test.pubkey)}); - BEAST_EXPECT(kp.second == SecretKey{makeSlice(test.seckey)}); - BEAST_EXPECT(calcAccountID(kp.first) == *id); + BEAST_EXPECT(test::equal(kp.second, SecretKey{makeSlice(test.seckey)})); + BEAST_EXPECT( + calcAccountID(kp.first) == *id); // NOLINT(bugprone-unchecked-optional-access) } } @@ -310,8 +320,9 @@ public: auto kp = generateKeyPair(KeyType::ed25519, Seed{makeSlice(test.seed)}); BEAST_EXPECT(kp.first == PublicKey{makeSlice(test.pubkey)}); - BEAST_EXPECT(kp.second == SecretKey{makeSlice(test.seckey)}); - BEAST_EXPECT(calcAccountID(kp.first) == *id); + BEAST_EXPECT(test::equal(kp.second, SecretKey{makeSlice(test.seckey)})); + BEAST_EXPECT( + calcAccountID(kp.first) == *id); // NOLINT(bugprone-unchecked-optional-access) } } diff --git a/src/test/protocol/Seed_test.cpp b/src/test/protocol/Seed_test.cpp index 9f0ce68c46..75c7e402a1 100644 --- a/src/test/protocol/Seed_test.cpp +++ b/src/test/protocol/Seed_test.cpp @@ -50,7 +50,7 @@ public: auto const seed2 = parseBase58(toBase58(seed1)); BEAST_EXPECT(static_cast(seed2)); - BEAST_EXPECT(equal(seed1, *seed2)); + BEAST_EXPECT(equal(seed1, *seed2)); // NOLINT(bugprone-unchecked-optional-access) return toBase58(seed1); } @@ -93,15 +93,15 @@ public: auto const seed2 = parseBase58(toBase58(seed1)); BEAST_EXPECT(static_cast(seed2)); - BEAST_EXPECT(equal(seed1, *seed2)); + BEAST_EXPECT(equal(seed1, *seed2)); // NOLINT(bugprone-unchecked-optional-access) } } void testKeypairGenerationAndSigning() { - std::string const message1 = "http://www.ripple.com"; - std::string const message2 = "https://www.ripple.com"; + std::string const message1 = "http://www.xrpl.org"; + std::string const message2 = "https://www.xrpl.org"; { testcase("Node keypair generation & signing (secp256k1)"); @@ -120,7 +120,7 @@ public: to_string(calcNodeID(publicKey)) == "7E59C17D50F5959C7B158FEC95C8F815BF653DC8"); auto sig = sign(publicKey, secretKey, makeSlice(message1)); - BEAST_EXPECT(sig.size() != 0); + BEAST_EXPECT(!sig.empty()); BEAST_EXPECT(verify(publicKey, makeSlice(message1), sig)); // Correct public key but wrong message @@ -162,7 +162,7 @@ public: to_string(calcNodeID(publicKey)) == "AA066C988C712815CC37AF71472B7CBBBD4E2A0A"); auto sig = sign(publicKey, secretKey, makeSlice(message1)); - BEAST_EXPECT(sig.size() != 0); + BEAST_EXPECT(!sig.empty()); BEAST_EXPECT(verify(publicKey, makeSlice(message1), sig)); // Correct public key but wrong message @@ -202,7 +202,7 @@ public: "p9JfM6HHi64m6mvB6v5k7G2b1cXzGmYiCNJf6GHPKvFTWdeRVjh"); auto sig = sign(pk, sk, makeSlice(message1)); - BEAST_EXPECT(sig.size() != 0); + BEAST_EXPECT(!sig.empty()); BEAST_EXPECT(verify(pk, makeSlice(message1), sig)); // Correct public key but wrong message @@ -241,7 +241,7 @@ public: "pwDQjwEhbUBmPuEjFpEG75bFhv2obkCB7NxQsfFxM7xGHBMVPu9"); auto sig = sign(pk, sk, makeSlice(message1)); - BEAST_EXPECT(sig.size() != 0); + BEAST_EXPECT(!sig.empty()); BEAST_EXPECT(verify(pk, makeSlice(message1), sig)); // Correct public key but wrong message diff --git a/src/test/protocol/SeqProxy_test.cpp b/src/test/protocol/SeqProxy_test.cpp index 9096fe7daf..7e7a21ab6e 100644 --- a/src/test/protocol/SeqProxy_test.cpp +++ b/src/test/protocol/SeqProxy_test.cpp @@ -42,7 +42,7 @@ struct SeqProxy_test : public beast::unit_test::suite } // Verify streaming. - bool + static bool streamTest(SeqProxy seqProx) { std::string const type{seqProx.isSeq() ? "sequence" : "ticket"}; diff --git a/src/test/protocol/Serializer_test.cpp b/src/test/protocol/Serializer_test.cpp index c5b56c3029..e4eaac8a58 100644 --- a/src/test/protocol/Serializer_test.cpp +++ b/src/test/protocol/Serializer_test.cpp @@ -17,7 +17,7 @@ struct Serializer_test : public beast::unit_test::suite 0, 1, std::numeric_limits::max()}; - for (std::int32_t value : values) + for (std::int32_t const value : values) { Serializer s; s.add32(value); @@ -33,7 +33,7 @@ struct Serializer_test : public beast::unit_test::suite 0, 1, std::numeric_limits::max()}; - for (std::int64_t value : values) + for (std::int64_t const value : values) { Serializer s; s.add64(value); diff --git a/src/test/protocol/TER_test.cpp b/src/test/protocol/TER_test.cpp index cf88a570c5..2ad4b634aa 100644 --- a/src/test/protocol/TER_test.cpp +++ b/src/test/protocol/TER_test.cpp @@ -13,7 +13,7 @@ struct TER_test : public beast::unit_test::suite { for (auto i = -400; i < 400; ++i) { - TER t = TER::fromInt(i); + TER const t = TER::fromInt(i); auto inRange = isTelLocal(t) || isTemMalformed(t) || isTefFailure(t) || isTerRetry(t) || isTesSuccess(t) || isTecClaim(t); @@ -75,7 +75,7 @@ struct TER_test : public beast::unit_test::suite std::enable_if_t testIterate(Tup const& tup, beast::unit_test::suite& s) { - Func func; + Func const func; func(tup, s); testIterate(tup, s); } @@ -89,7 +89,7 @@ struct TER_test : public beast::unit_test::suite std::enable_if_t testIterate(Tup const& tup, beast::unit_test::suite& s) { - Func func; + Func const func; func(tup, s); testIterate::value - 1, I2 - 1, Func>(tup, s); } @@ -103,7 +103,7 @@ struct TER_test : public beast::unit_test::suite std::enable_if_t testIterate(Tup const& tup, beast::unit_test::suite& s) { - Func func; + Func const func; func(tup, s); } diff --git a/src/test/resource/Logic_test.cpp b/src/test/resource/Logic_test.cpp index de57c715bc..12c1e631e2 100644 --- a/src/test/resource/Logic_test.cpp +++ b/src/test/resource/Logic_test.cpp @@ -44,7 +44,7 @@ public: //-------------------------------------------------------------------------- - void + static void createGossip(Gossip& gossip) { std::uint8_t const v(10 + rand_int(9)); @@ -54,7 +54,8 @@ public: { Gossip::Item item; item.balance = 100 + rand_int(499); - beast::IP::AddressV4::bytes_type d = {{192, 0, 2, static_cast(v + i)}}; + beast::IP::AddressV4::bytes_type const d = { + {192, 0, 2, static_cast(v + i)}}; item.address = beast::IP::Endpoint{beast::IP::AddressV4{d}}; gossip.items.push_back(item); } @@ -66,16 +67,20 @@ public: testDrop(beast::Journal j, bool limited) { if (limited) + { testcase("Limited warn/drop"); + } else + { testcase("Unlimited warn/drop"); + } TestLogic logic(j); Charge const fee(dropThreshold + 1); beast::IP::Endpoint const addr(beast::IP::Endpoint::from_string("192.0.2.2")); - std::function ep = limited + std::function const ep = limited ? std::bind(&TestLogic::newInboundEndpoint, &logic, std::placeholders::_1) : std::bind(&TestLogic::newUnlimitedEndpoint, &logic, std::placeholders::_1); @@ -90,18 +95,26 @@ public: if (n == 0) { if (limited) + { fail("Loop count exceeded without warning"); + } else + { pass(); + } return; } if (c.charge(fee) == warn) { if (limited) + { pass(); + } else + { fail("Should loop forever with no warning"); + } break; } ++logic.clock(); @@ -113,9 +126,13 @@ public: if (n == 0) { if (limited) + { fail("Loop count exceeded without dropping"); + } else + { pass(); + } return; } @@ -131,14 +148,18 @@ public: // Make sure the consumer is on the blacklist for a while. { - Consumer c(logic.newInboundEndpoint(addr)); + Consumer const c(logic.newInboundEndpoint(addr)); logic.periodicActivity(); if (c.disposition() != drop) { if (limited) + { fail("Dropped consumer not put on blacklist"); + } else + { pass(); + } return; } } @@ -154,7 +175,7 @@ public: { ++logic.clock(); logic.periodicActivity(); - Consumer c(logic.newInboundEndpoint(addr)); + Consumer const c(logic.newInboundEndpoint(addr)); if (c.disposition() != drop) { readmitted = true; @@ -162,7 +183,7 @@ public: } } } - if (readmitted == false) + if (!readmitted) { fail("Dropped Consumer left on blacklist too long"); return; @@ -198,7 +219,7 @@ public: Gossip g; Gossip::Item item; item.balance = 100; - beast::IP::AddressV4::bytes_type d = {{192, 0, 2, 1}}; + beast::IP::AddressV4::bytes_type const d = {{192, 0, 2, 1}}; item.address = beast::IP::Endpoint{beast::IP::AddressV4{d}}; g.items.push_back(item); @@ -215,9 +236,9 @@ public: TestLogic logic(j); { - beast::IP::Endpoint address(beast::IP::Endpoint::from_string("192.0.2.1")); + beast::IP::Endpoint const address(beast::IP::Endpoint::from_string("192.0.2.1")); Consumer c(logic.newInboundEndpoint(address)); - Charge fee(1000); + Charge const fee(1000); JLOG(j.info()) << "Charging " << c.to_string() << " " << fee << " per second"; c.charge(fee); for (int i = 0; i < 128; ++i) @@ -229,9 +250,9 @@ public: } { - beast::IP::Endpoint address(beast::IP::Endpoint::from_string("192.0.2.2")); + beast::IP::Endpoint const address(beast::IP::Endpoint::from_string("192.0.2.2")); Consumer c(logic.newInboundEndpoint(address)); - Charge fee(1000); + Charge const fee(1000); JLOG(j.info()) << "Charging " << c.to_string() << " " << fee << " per second"; for (int i = 0; i < 128; ++i) { diff --git a/src/test/rpc/AMMInfo_test.cpp b/src/test/rpc/AMMInfo_test.cpp index d3be04ff96..3f20e0378d 100644 --- a/src/test/rpc/AMMInfo_test.cpp +++ b/src/test/rpc/AMMInfo_test.cpp @@ -23,11 +23,15 @@ public: enum TestAccount { None, Alice, Bogie }; auto accountId = [&](AMM const& ammAlice, TestAccount v) -> std::optional { if (v == Alice) + { return ammAlice.ammAccount(); - else if (v == Bogie) + } + if (v == Bogie) + { return bogie; - else - return std::nullopt; + } + + return std::nullopt; }; // Invalid tokens pair @@ -185,9 +189,13 @@ public: Account a(std::to_string(i)); votes.insert({a.human(), 50 * (i + 1)}); if (!features[fixAMMv1_3]) + { fund(env, gw, {a}, {USD(10000)}, Fund::Acct); + } else + { fund(env, gw, {a}, {USD(10001)}, Fund::Acct); + } ammAlice.deposit(a, 10000000); ammAlice.vote(a, 50 * (i + 1)); } @@ -197,6 +205,7 @@ public: env.fund(XRP(1000), bob, ed, bill); env(ammAlice.bid({.bidMin = 100, .authAccounts = {carol, bob, ed, bill}})); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectAmmRpcInfo( XRP(80000), USD(80000), @@ -204,7 +213,9 @@ public: std::nullopt, std::nullopt, ammAlice.ammAccount())); + } else + { BEAST_EXPECT(ammAlice.expectAmmRpcInfo( XRPAmount(80000000005), STAmount{USD, UINT64_C(80'000'00000000005), -11}, @@ -212,6 +223,7 @@ public: std::nullopt, std::nullopt, ammAlice.ammAccount())); + } for (auto i = 0; i < 2; ++i) { std::unordered_set authAccounts = { diff --git a/src/test/rpc/AccountCurrencies_test.cpp b/src/test/rpc/AccountCurrencies_test.cpp index c317bfc31c..009af9d454 100644 --- a/src/test/rpc/AccountCurrencies_test.cpp +++ b/src/test/rpc/AccountCurrencies_test.cpp @@ -142,7 +142,7 @@ class AccountCurrencies_test : public beast::unit_test::suite // now form a payment for each currency for (auto const& c : gwCurrencies) - env(pay(gw, alice, c.value()(50))); + env(pay(gw, alice, c.value()(50))); // NOLINT(bugprone-unchecked-optional-access) // send_currencies should be populated now result = env.rpc("json", "account_currencies", to_string(params))[jss::result]; @@ -165,7 +165,8 @@ class AccountCurrencies_test : public beast::unit_test::suite env(pay(gw, alice, gw["USA"](50))); // USA should now be missing from receive_currencies result = env.rpc("json", "account_currencies", to_string(params))[jss::result]; - decltype(gwCurrencies) gwCurrenciesNoUSA(gwCurrencies.begin() + 1, gwCurrencies.end()); + decltype(gwCurrencies) + const gwCurrenciesNoUSA(gwCurrencies.begin() + 1, gwCurrencies.end()); BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrenciesNoUSA)); BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies)); diff --git a/src/test/rpc/AccountInfo_test.cpp b/src/test/rpc/AccountInfo_test.cpp index 76079a9023..518398f9ea 100644 --- a/src/test/rpc/AccountInfo_test.cpp +++ b/src/test/rpc/AccountInfo_test.cpp @@ -529,7 +529,7 @@ public: env.close(); auto const f1 = getAccountFlag(asf.first, alice); BEAST_EXPECT(f1.has_value()); - BEAST_EXPECT(!f1.value()); + BEAST_EXPECT(!f1.value()); // NOLINT(bugprone-unchecked-optional-access) // Set a flag and check that account_info returns results // as expected @@ -537,7 +537,7 @@ public: env.close(); auto const f2 = getAccountFlag(asf.first, alice); BEAST_EXPECT(f2.has_value()); - BEAST_EXPECT(f2.value()); + BEAST_EXPECT(f2.value()); // NOLINT(bugprone-unchecked-optional-access) } static constexpr std::array, 4> @@ -555,7 +555,7 @@ public: env.close(); auto const f1 = getAccountFlag(asf.first, alice); BEAST_EXPECT(f1.has_value()); - BEAST_EXPECT(!f1.value()); + BEAST_EXPECT(!f1.value()); // NOLINT(bugprone-unchecked-optional-access) // Set a flag and check that account_info returns results // as expected @@ -563,7 +563,7 @@ public: env.close(); auto const f2 = getAccountFlag(asf.first, alice); BEAST_EXPECT(f2.has_value()); - BEAST_EXPECT(f2.value()); + BEAST_EXPECT(f2.value()); // NOLINT(bugprone-unchecked-optional-access) } static constexpr std::pair allowTrustLineClawbackFlag{ @@ -574,14 +574,14 @@ public: // must use bob's account because alice has noFreeze set auto const f1 = getAccountFlag(allowTrustLineClawbackFlag.first, bob); BEAST_EXPECT(f1.has_value()); - BEAST_EXPECT(!f1.value()); + BEAST_EXPECT(!f1.value()); // NOLINT(bugprone-unchecked-optional-access) // Set allowTrustLineClawback env(fset(bob, allowTrustLineClawbackFlag.second)); env.close(); auto const f2 = getAccountFlag(allowTrustLineClawbackFlag.first, bob); BEAST_EXPECT(f2.has_value()); - BEAST_EXPECT(f2.value()); + BEAST_EXPECT(f2.value()); // NOLINT(bugprone-unchecked-optional-access) } else { @@ -595,14 +595,14 @@ public: { auto const f1 = getAccountFlag(allowTrustLineLockingFlag.first, bob); BEAST_EXPECT(f1.has_value()); - BEAST_EXPECT(!f1.value()); + BEAST_EXPECT(!f1.value()); // NOLINT(bugprone-unchecked-optional-access) // Set allowTrustLineLocking env(fset(bob, allowTrustLineLockingFlag.second)); env.close(); auto const f2 = getAccountFlag(allowTrustLineLockingFlag.first, bob); BEAST_EXPECT(f2.has_value()); - BEAST_EXPECT(f2.value()); + BEAST_EXPECT(f2.value()); // NOLINT(bugprone-unchecked-optional-access) } else { diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index 090f2a43b9..b6e8f6fa76 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -107,7 +107,7 @@ public: // test error on no account { - Json::Value params; + Json::Value const params; auto resp = env.rpc("json", "account_objects", to_string(params)); BEAST_EXPECT(resp[jss::result][jss::error_message] == "Missing field 'account'."); } @@ -305,9 +305,13 @@ public: BEAST_EXPECT(aobjs.size() == 1); auto& aobj = aobjs[0U]; if (i < 3) + { BEAST_EXPECT(resp[jss::result][jss::limit] == 1); + } else + { BEAST_EXPECT(!resp[jss::result].isMember(jss::limit)); + } aobj.removeMember("PreviousTxnID"); aobj.removeMember("PreviousTxnLgrSeq"); @@ -484,7 +488,7 @@ public: params[jss::type] = jss::nft_page; auto resp = env.rpc("json", "account_objects", to_string(params)); BEAST_EXPECT(!resp.isMember(jss::marker)); - Json::Value& aobjs = resp[jss::result][jss::account_objects]; + Json::Value const& aobjs = resp[jss::result][jss::account_objects]; BEAST_EXPECT(aobjs.size() == 2); } // test stepped one-at-a-time with limit=1, resume from prev marker @@ -1272,7 +1276,7 @@ public: // valid, because when dirIndex = 0, we will use root key to find // dir. { - std::string s = "0," + entryIndex; + std::string const s = "0," + entryIndex; Json::Value params; params[jss::account] = bob.human(); params[jss::limit] = limit; @@ -1309,7 +1313,7 @@ public: auto resp = env.rpc("json", "account_objects", to_string(params)); auto& accountObjects = resp[jss::result][jss::account_objects]; BEAST_EXPECT(!resp[jss::result].isMember(jss::error)); - BEAST_EXPECT(accountObjects.size() == accountObjectSize - limit * 2); + BEAST_EXPECT(accountObjects.size() == accountObjectSize - (limit * 2)); BEAST_EXPECT(!resp[jss::result].isMember(jss::marker)); } diff --git a/src/test/rpc/AccountOffers_test.cpp b/src/test/rpc/AccountOffers_test.cpp index 55e60f8caa..6b93e0570f 100644 --- a/src/test/rpc/AccountOffers_test.cpp +++ b/src/test/rpc/AccountOffers_test.cpp @@ -13,7 +13,7 @@ public: checkMarker(Json::Value const& val) { return val.isMember(jss::marker) && val[jss::marker].isString() && - val[jss::marker].asString().size() > 0; + !val[jss::marker].asString().empty(); } void diff --git a/src/test/rpc/AccountTx_test.cpp b/src/test/rpc/AccountTx_test.cpp index 5015b960f9..2470ec3c4c 100644 --- a/src/test/rpc/AccountTx_test.cpp +++ b/src/test/rpc/AccountTx_test.cpp @@ -62,19 +62,24 @@ class AccountTx_test : public beast::unit_test::suite for (Json::Value const& metaNode : txNode[jss::meta][sfAffectedNodes.jsonName]) { if (metaNode.isMember(sfCreatedNode.jsonName)) + { createdNodes.insert( metaNode[sfCreatedNode.jsonName][sfLedgerEntryType.jsonName].asString()); - + } else if (metaNode.isMember(sfDeletedNode.jsonName)) + { deletedNodes.insert( metaNode[sfDeletedNode.jsonName][sfLedgerEntryType.jsonName].asString()); - + } else if (metaNode.isMember(sfModifiedNode.jsonName)) + { modifiedNodes.insert( metaNode[sfModifiedNode.jsonName][sfLedgerEntryType.jsonName].asString()); - + } else + { fail("Unexpected or unlabeled node type in metadata.", __FILE__, __LINE__); + } } BEAST_EXPECT(createdNodes == sane.created); @@ -92,7 +97,7 @@ class AccountTx_test : public beast::unit_test::suite cfg->FEES.reference_fee = 10; return cfg; })); - Account A1{"A1"}; + Account const A1{"A1"}; env.fund(XRP(10000), A1); env.close(); @@ -138,7 +143,9 @@ class AccountTx_test : public beast::unit_test::suite (payment[jss::close_time_iso] == "2000-01-01T00:00:10Z"); } else + { return false; + } default: return false; @@ -177,18 +184,26 @@ class AccountTx_test : public beast::unit_test::suite p[jss::ledger_index_min] = 0; p[jss::ledger_index_max] = 100; if (apiVersion < 2u) + { BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p)))); + } else + { BEAST_EXPECT( isErr(env.rpc("json", "account_tx", to_string(p)), rpcLGR_IDX_MALFORMED)); + } p[jss::ledger_index_min] = 1; p[jss::ledger_index_max] = 2; if (apiVersion < 2u) + { BEAST_EXPECT(noTxs(env.rpc("json", "account_tx", to_string(p)))); + } else + { BEAST_EXPECT( isErr(env.rpc("json", "account_tx", to_string(p)), rpcLGR_IDX_MALFORMED)); + } p[jss::ledger_index_min] = 2; p[jss::ledger_index_max] = 1; @@ -204,10 +219,14 @@ class AccountTx_test : public beast::unit_test::suite p[jss::ledger_index_min] = 1; if (apiVersion < 2u) + { BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p)))); + } else + { BEAST_EXPECT( isErr(env.rpc("json", "account_tx", to_string(p)), rpcLGR_IDX_MALFORMED)); + } p[jss::ledger_index_min] = env.current()->header().seq; BEAST_EXPECT(isErr( @@ -223,10 +242,14 @@ class AccountTx_test : public beast::unit_test::suite p[jss::ledger_index_max] = env.current()->header().seq; if (apiVersion < 2u) + { BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p)))); + } else + { BEAST_EXPECT( isErr(env.rpc("json", "account_tx", to_string(p)), rpcLGR_IDX_MALFORMED)); + } p[jss::ledger_index_max] = 3; BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p)))); @@ -278,9 +301,13 @@ class AccountTx_test : public beast::unit_test::suite p[jss::ledger_index] = -1; if (apiVersion < 2u) + { BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p)))); + } else + { BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), rpcINVALID_PARAMS)); + } } // Ledger index max only @@ -288,10 +315,14 @@ class AccountTx_test : public beast::unit_test::suite Json::Value p{jParams}; p[jss::ledger_index_max] = env.current()->header().seq; if (apiVersion < 2u) + { BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p)))); + } else + { BEAST_EXPECT( isErr(env.rpc("json", "account_tx", to_string(p)), rpcLGR_IDX_MALFORMED)); + } } // test account non-string { @@ -320,7 +351,9 @@ class AccountTx_test : public beast::unit_test::suite BEAST_EXPECT(result[jss::result][jss::status] == "success"); } else + { BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), rpcINVALID_PARAMS)); + } p[jss::binary] = true; Json::Value result{env.rpc("json", "account_tx", to_string(p))}; @@ -328,9 +361,13 @@ class AccountTx_test : public beast::unit_test::suite p[jss::forward] = "true"; if (apiVersion < 2u) + { BEAST_EXPECT(result[jss::result][jss::status] == "success"); + } else + { BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), rpcINVALID_PARAMS)); + } p[jss::forward] = false; result = env.rpc("json", "account_tx", to_string(p)); @@ -504,7 +541,7 @@ class AccountTx_test : public beast::unit_test::suite // PayChan { - std::uint32_t payChanSeq{env.seq(alice)}; + std::uint32_t const payChanSeq{env.seq(alice)}; Json::Value payChanCreate; payChanCreate[jss::TransactionType] = jss::PaymentChannelCreate; payChanCreate[jss::Account] = alice.human(); diff --git a/src/test/rpc/BookChanges_test.cpp b/src/test/rpc/BookChanges_test.cpp index 6bd71b141b..0618f4e7d0 100644 --- a/src/test/rpc/BookChanges_test.cpp +++ b/src/test/rpc/BookChanges_test.cpp @@ -78,7 +78,7 @@ public: featurePermissionedDEX}; Env env(*this, all); - PermissionedDEX permDex(env); + PermissionedDEX const permDex(env); auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = permDex; auto wsc = makeWSClient(env.app().config()); diff --git a/src/test/rpc/Book_test.cpp b/src/test/rpc/Book_test.cpp index c9d93b87a6..5e149ebc0e 100644 --- a/src/test/rpc/Book_test.cpp +++ b/src/test/rpc/Book_test.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -14,7 +15,7 @@ namespace test { class Book_test : public beast::unit_test::suite { - std::string + static std::string getBookDir( jtx::Env& env, Issue const& in, @@ -30,7 +31,7 @@ class Book_test : public beast::unit_test::suite { auto sleOfferDir = view->read(keylet::page(key.value())); uint256 offerIndex; - unsigned int bookEntry; + unsigned int bookEntry = 0; cdirFirst(*view, sleOfferDir->key(), sleOfferDir, bookEntry, offerIndex); auto sleOffer = view->read(keylet::offer(offerIndex)); dir = to_string(sleOffer->getFieldH256(sfBookDirectory)); @@ -876,9 +877,9 @@ public: testcase("TrackOffers"); using namespace jtx; Env env(*this); - Account gw{"gw"}; - Account alice{"alice"}; - Account bob{"bob"}; + Account const gw{"gw"}; + Account const alice{"alice"}; + Account const bob{"bob"}; auto wsc = makeWSClient(env.app().config()); env.fund(XRP(20000), alice, bob, gw); env.close(); @@ -1185,8 +1186,8 @@ public: testcase("BookOffersRPC Errors"); using namespace jtx; Env env(*this); - Account gw{"gw"}; - Account alice{"alice"}; + Account const gw{"gw"}; + Account const alice{"alice"}; env.fund(XRP(10000), alice, gw); env.close(); auto USD = gw["USD"]; @@ -1514,7 +1515,7 @@ public: testcase("BookOffer Limits"); using namespace jtx; Env env{*this, asAdmin ? envconfig() : envconfig(no_admin)}; - Account gw{"gw"}; + Account const gw{"gw"}; env.fund(XRP(200000), gw); // Note that calls to env.close() fail without admin permission. if (asAdmin) @@ -1523,7 +1524,7 @@ public: auto USD = gw["USD"]; for (auto i = 0; i <= RPC::Tuning::bookOffers.rmax; i++) - env(offer(gw, XRP(50 + 1 * i), USD(1.0 + 0.1 * i))); + env(offer(gw, XRP(50 + (1 * i)), USD(1.0 + (0.1 * i)))); if (asAdmin) env.close(); @@ -1561,7 +1562,7 @@ public: featurePermissionedDEX}; Env env(*this, all); - PermissionedDEX permDex(env); + PermissionedDEX const permDex(env); auto const alice = permDex.alice; auto const bob = permDex.bob; auto const carol = permDex.carol; @@ -1684,7 +1685,7 @@ public: featurePermissionedDEX}; Env env(*this, all); - PermissionedDEX permDex(env); + PermissionedDEX const permDex(env); auto const alice = permDex.alice; auto const bob = permDex.bob; auto const carol = permDex.carol; diff --git a/src/test/rpc/DeliveredAmount_test.cpp b/src/test/rpc/DeliveredAmount_test.cpp index c156a566b0..358657a6c3 100644 --- a/src/test/rpc/DeliveredAmount_test.cpp +++ b/src/test/rpc/DeliveredAmount_test.cpp @@ -36,9 +36,13 @@ class CheckDeliveredAmount if (!afterSwitchTime_) { if (partial) + { ++numExpectedAvailable_; + } else + { ++numExpectedSetUnavailable_; + } return; } // normal case: after switch time & successful transaction @@ -72,7 +76,8 @@ public: bool checkExpectedCounters() const { - return !numExpectedAvailable_ && !numExpectedNotSet_ && !numExpectedSetUnavailable_; + return (numExpectedAvailable_ == 0) && (numExpectedNotSet_ == 0) && + (numExpectedSetUnavailable_ == 0); } // Check if the transaction has `delivered_amount` in the metaData as @@ -85,22 +90,32 @@ public: if (t[jss::TransactionType].asString() != jss::Payment) return true; - bool isSet = metaData.isMember(jss::delivered_amount); + bool const isSet = metaData.isMember(jss::delivered_amount); bool isSetUnavailable = false; bool isSetAvailable = false; if (isSet) { if (metaData[jss::delivered_amount] != "unavailable") + { isSetAvailable = true; + } else + { isSetUnavailable = true; + } } if (isSetAvailable) + { --numExpectedAvailable_; + } else if (isSetUnavailable) + { --numExpectedSetUnavailable_; + } else if (!isSet) + { --numExpectedNotSet_; + } if (isSet) { @@ -178,9 +193,13 @@ class DeliveredAmount_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob, carol, gw); env.trust(USD(1000), alice, bob, carol); if (afterSwitchTime) + { env.close(NetClock::time_point{446000000s}); + } else + { env.close(); + } CheckDeliveredAmount checkDeliveredAmount{afterSwitchTime}; { @@ -265,9 +284,13 @@ class DeliveredAmount_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob, carol, gw); env.trust(USD(1000), alice, bob, carol); if (afterSwitchTime) + { env.close(NetClock::time_point{446000000s}); + } else + { env.close(); + } CheckDeliveredAmount checkDeliveredAmount{afterSwitchTime}; // normal payments diff --git a/src/test/rpc/DepositAuthorized_test.cpp b/src/test/rpc/DepositAuthorized_test.cpp index 9d6de94ccc..7921d063c9 100644 --- a/src/test/rpc/DepositAuthorized_test.cpp +++ b/src/test/rpc/DepositAuthorized_test.cpp @@ -191,14 +191,14 @@ public: } { // Request an invalid ledger. - Json::Value args{depositAuthArgs(alice, becky, "-1")}; + Json::Value const args{depositAuthArgs(alice, becky, "-1")}; Json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())}; verifyErr( result, "invalidParams", "Invalid field 'ledger_index', not string or number."); } { // Request a ledger that doesn't exist yet as a string. - Json::Value args{depositAuthArgs(alice, becky, "17")}; + Json::Value const args{depositAuthArgs(alice, becky, "17")}; Json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())}; verifyErr(result, "lgrNotFound", "ledgerNotFound"); } @@ -211,7 +211,7 @@ public: } { // alice is not yet funded. - Json::Value args{depositAuthArgs(alice, becky)}; + Json::Value const args{depositAuthArgs(alice, becky)}; Json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())}; verifyErr(result, "srcActNotFound", "Source account not found."); } @@ -219,7 +219,7 @@ public: env.close(); { // becky is not yet funded. - Json::Value args{depositAuthArgs(alice, becky)}; + Json::Value const args{depositAuthArgs(alice, becky)}; Json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())}; verifyErr(result, "dstActNotFound", "Destination account not found."); } @@ -227,7 +227,7 @@ public: env.close(); { // Once becky is funded try it again and see it succeed. - Json::Value args{depositAuthArgs(alice, becky)}; + Json::Value const args{depositAuthArgs(alice, becky)}; Json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())}; validateDepositAuthResult(result, true); } @@ -246,9 +246,11 @@ public: if (result.isMember(jss::deposit_authorized)) BEAST_EXPECT(result[jss::deposit_authorized] == authorized); if (authorized) + { BEAST_EXPECT( result.isMember(jss::deposit_authorized) && (result[jss::deposit_authorized] == true)); + } BEAST_EXPECT(result.isMember(jss::error) == !error.empty()); if (!error.empty()) diff --git a/src/test/rpc/Feature_test.cpp b/src/test/rpc/Feature_test.cpp index a802aed482..657eda2c1c 100644 --- a/src/test/rpc/Feature_test.cpp +++ b/src/test/rpc/Feature_test.cpp @@ -2,6 +2,7 @@ #include #include +#include #include namespace xrpl { @@ -94,7 +95,8 @@ class Feature_test : public beast::unit_test::suite { (void)vote; auto const registered = getRegisteredFeature(feature); - if (BEAST_EXPECT(registered)) + + if (BEAST_EXPECT(registered); registered.has_value()) { BEAST_EXPECT(featureToName(*registered) == feature); BEAST_EXPECT( @@ -103,7 +105,7 @@ class Feature_test : public beast::unit_test::suite } // Test an arbitrary unknown feature - uint256 zero{0}; + uint256 const zero{0}; BEAST_EXPECT(featureToName(zero) == to_string(zero)); BEAST_EXPECT( featureToName(zero) == @@ -141,8 +143,9 @@ class Feature_test : public beast::unit_test::suite return; // default config - so all should be disabled, and // supported. Some may be vetoed. - bool expectVeto = (votes.at(feature[jss::name].asString()) == VoteBehavior::DefaultNo); - bool expectObsolete = + bool const expectVeto = + (votes.at(feature[jss::name].asString()) == VoteBehavior::DefaultNo); + bool const expectObsolete = (votes.at(feature[jss::name].asString()) == VoteBehavior::Obsolete); BEAST_EXPECTS( feature.isMember(jss::enabled) && !feature[jss::enabled].asBool(), @@ -168,16 +171,18 @@ class Feature_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this}; - auto jrr = env.rpc("feature", "fixAMMOverflowOffer")[jss::result]; + std::string const name = "fixAMMOverflowOffer"; + auto jrr = env.rpc("feature", name)[jss::result]; BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"); jrr.removeMember(jss::status); BEAST_EXPECT(jrr.size() == 1); - BEAST_EXPECT(jrr.isMember( - "12523DF04B553A0B1AD74F42DDB741DE8DC06A03FC089A0EF197E" - "2A87F1D8107")); + auto const expected = to_string(sha512Half(Slice(name.data(), name.size()))); + char const sha[] = "12523DF04B553A0B1AD74F42DDB741DE8DC06A03FC089A0EF197E2A87F1D8107"; + BEAST_EXPECT(expected == sha); + BEAST_EXPECT(jrr.isMember(expected)); auto feature = *(jrr.begin()); - BEAST_EXPECTS(feature[jss::name] == "fixAMMOverflowOffer", "name"); + BEAST_EXPECTS(feature[jss::name] == name, "name"); BEAST_EXPECTS(!feature[jss::enabled].asBool(), "enabled"); BEAST_EXPECTS(feature[jss::vetoed].isBool() && !feature[jss::vetoed].asBool(), "vetoed"); BEAST_EXPECTS(feature[jss::supported].asBool(), "supported"); @@ -186,6 +191,37 @@ class Feature_test : public beast::unit_test::suite jrr = env.rpc("feature", "fMM")[jss::result]; BEAST_EXPECT(jrr[jss::error] == "badFeature"); BEAST_EXPECT(jrr[jss::error_message] == "Feature unknown or invalid."); + + // Test feature name size checks + constexpr auto ok63Name = [] { + return "123456789012345678901234567890123456789012345678901234567890123"; + }; + static_assert(validFeatureNameSize(ok63Name)); + + constexpr auto bad64Name = [] { + return "1234567890123456789012345678901234567890123456789012345678901234"; + }; + static_assert(!validFeatureNameSize(bad64Name)); + + constexpr auto ok31Name = [] { return "1234567890123456789012345678901"; }; + static_assert(validFeatureNameSize(ok31Name)); + + constexpr auto bad32Name = [] { return "12345678901234567890123456789012"; }; + static_assert(!validFeatureNameSize(bad32Name)); + + constexpr auto ok33Name = [] { return "123456789012345678901234567890123"; }; + static_assert(validFeatureNameSize(ok33Name)); + + // Test feature character set checks + constexpr auto okName = [] { return "AMM_123"; }; + static_assert(validFeatureName(okName)); + + // First character is Greek Capital Alpha, visually confusable with ASCII 'A' + constexpr auto badName = [] { return "ΑMM_123"; }; + static_assert(!validFeatureName(badName)); + + constexpr auto badEmoji = [] { return "🔥"; }; + static_assert(!validFeatureName(badEmoji)); } void @@ -243,8 +279,8 @@ class Feature_test : public beast::unit_test::suite (void)id.parseHex(it.key().asString().c_str()); if (!BEAST_EXPECT((*it).isMember(jss::name))) return; - bool expectEnabled = env.app().getAmendmentTable().isEnabled(id); - bool expectSupported = env.app().getAmendmentTable().isSupported(id); + bool const expectEnabled = env.app().getAmendmentTable().isEnabled(id); + bool const expectSupported = env.app().getAmendmentTable().isSupported(id); BEAST_EXPECTS( (*it).isMember(jss::enabled) && (*it)[jss::enabled].asBool() == expectEnabled, (*it)[jss::name].asString() + " enabled"); @@ -304,17 +340,22 @@ class Feature_test : public beast::unit_test::suite (void)id.parseHex(it.key().asString().c_str()); if (!BEAST_EXPECT((*it).isMember(jss::name))) return; - bool expectEnabled = env.app().getAmendmentTable().isEnabled(id); - bool expectSupported = env.app().getAmendmentTable().isSupported(id); - bool expectVeto = (votes.at((*it)[jss::name].asString()) == VoteBehavior::DefaultNo); - bool expectObsolete = (votes.at((*it)[jss::name].asString()) == VoteBehavior::Obsolete); + bool const expectEnabled = env.app().getAmendmentTable().isEnabled(id); + bool const expectSupported = env.app().getAmendmentTable().isSupported(id); + bool const expectVeto = + (votes.at((*it)[jss::name].asString()) == VoteBehavior::DefaultNo); + bool const expectObsolete = + (votes.at((*it)[jss::name].asString()) == VoteBehavior::Obsolete); BEAST_EXPECTS( (*it).isMember(jss::enabled) && (*it)[jss::enabled].asBool() == expectEnabled, (*it)[jss::name].asString() + " enabled"); if (expectEnabled) + { BEAST_EXPECTS( !(*it).isMember(jss::vetoed), (*it)[jss::name].asString() + " vetoed"); + } else + { BEAST_EXPECTS( (*it).isMember(jss::vetoed) && (*it)[jss::vetoed].isBool() == !expectObsolete && (!(*it)[jss::vetoed].isBool() || @@ -322,6 +363,7 @@ class Feature_test : public beast::unit_test::suite ((*it)[jss::vetoed].isBool() || (*it)[jss::vetoed].asString() == "Obsolete"), (*it)[jss::name].asString() + " vetoed"); + } BEAST_EXPECTS( (*it).isMember(jss::supported) && (*it)[jss::supported].asBool() == expectSupported, (*it)[jss::name].asString() + " supported"); @@ -382,8 +424,9 @@ class Feature_test : public beast::unit_test::suite { if (!BEAST_EXPECT(feature.isMember(jss::name))) return; - bool expectVeto = (votes.at(feature[jss::name].asString()) == VoteBehavior::DefaultNo); - bool expectObsolete = + bool const expectVeto = + (votes.at(feature[jss::name].asString()) == VoteBehavior::DefaultNo); + bool const expectObsolete = (votes.at(feature[jss::name].asString()) == VoteBehavior::Obsolete); BEAST_EXPECTS( (expectVeto || expectObsolete) ^ feature.isMember(jss::majority), diff --git a/src/test/rpc/GatewayBalances_test.cpp b/src/test/rpc/GatewayBalances_test.cpp index 0deb1fc627..b6efea17fc 100644 --- a/src/test/rpc/GatewayBalances_test.cpp +++ b/src/test/rpc/GatewayBalances_test.cpp @@ -231,7 +231,7 @@ public: using namespace jtx; // Ensure MPT is enabled - FeatureBitset features = testable_amendments() | featureMPTokensV1; + FeatureBitset const features = testable_amendments() | featureMPTokensV1; Env env(*this, features); Account const alice{"alice"}; diff --git a/src/test/rpc/GetAggregatePrice_test.cpp b/src/test/rpc/GetAggregatePrice_test.cpp index b900afa178..1d14678f76 100644 --- a/src/test/rpc/GetAggregatePrice_test.cpp +++ b/src/test/rpc/GetAggregatePrice_test.cpp @@ -34,7 +34,7 @@ public: BEAST_EXPECT(ret[jss::error_message].asString() == "Missing field 'quote_asset'."); // invalid base_asset, quote_asset - std::vector invalidAsset = { + std::vector const invalidAsset = { NoneTag, 1, -1, @@ -76,7 +76,7 @@ public: ret = Oracle::aggregatePrice(env, "XRP", "USD", {{{owner, 2}}}); BEAST_EXPECT(ret[jss::error].asString() == "objectNotFound"); // invalid values - std::vector invalidDocument = {NoneTag, 1.2, -1, "", "none", "1.2"}; + std::vector const invalidDocument = {NoneTag, 1.2, -1, "", "none", "1.2"}; for (auto const& v : invalidDocument) { ret = Oracle::aggregatePrice(env, "XRP", "USD", {{{owner, v}}}); @@ -97,7 +97,7 @@ public: // oracles have wrong asset pair env.fund(XRP(1'000), owner); - Oracle oracle( + Oracle const oracle( env, {.owner = owner, .series = {{"XRP", "EUR", 740, 1}}, @@ -106,7 +106,7 @@ public: BEAST_EXPECT(ret[jss::error].asString() == "objectNotFound"); // invalid trim value - std::vector invalidTrim = {NoneTag, 0, 26, -1, 1.2, "", "none", "1.2"}; + std::vector const invalidTrim = {NoneTag, 0, 26, -1, 1.2, "", "none", "1.2"}; for (auto const& v : invalidTrim) { ret = @@ -115,7 +115,7 @@ public: } // invalid time threshold value - std::vector invalidTime = {NoneTag, -1, 1.2, "", "none", "1.2"}; + std::vector const invalidTime = {NoneTag, -1, 1.2, "", "none", "1.2"}; for (auto const& v : invalidTime) { ret = Oracle::aggregatePrice( @@ -134,7 +134,7 @@ public: { Account const owner(std::to_string(i)); env.fund(XRP(1'000), owner); - Oracle oracle(env, {.owner = owner, .documentID = i, .fee = baseFee}); + Oracle const oracle(env, {.owner = owner, .documentID = i, .fee = baseFee}); oracles.emplace_back(owner, oracle.documentID()); } auto const ret = Oracle::aggregatePrice(env, "XRP", "USD", oracles); @@ -156,7 +156,7 @@ public: Account const owner{std::to_string(i)}; env.fund(XRP(1'000), owner); - Oracle oracle( + Oracle const oracle( env, {.owner = owner, .documentID = rand(), @@ -178,7 +178,7 @@ public: // the global mantissa size. And since it's a thread-local, // overriding it locally won't make a difference either. // This will mean all RPC will use the default of "large". - NumberMantissaScaleGuard mg(mantissaSize); + NumberMantissaScaleGuard const mg(mantissaSize); Env env(*this, feats); OraclesData oracles; @@ -233,6 +233,7 @@ public: Oracle oracle( env, {.owner = oracles[i].first, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) .documentID = asUInt(*oracles[i].second), .fee = baseFee}, false); @@ -247,6 +248,7 @@ public: Oracle oracle( env, {.owner = oracles[i].first, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) .documentID = asUInt(*oracles[i].second), .fee = baseFee}, false); @@ -286,6 +288,7 @@ public: Oracle oracle( env, {.owner = oracles[i].first, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) .documentID = asUInt(*oracles[i].second), .fee = baseFee}, false); diff --git a/src/test/rpc/GetCounts_test.cpp b/src/test/rpc/GetCounts_test.cpp index 07c29e3648..d0729b3f56 100644 --- a/src/test/rpc/GetCounts_test.cpp +++ b/src/test/rpc/GetCounts_test.cpp @@ -32,8 +32,8 @@ class GetCounts_test : public beast::unit_test::suite // create some transactions env.close(); - Account alice{"alice"}; - Account bob{"bob"}; + Account const alice{"alice"}; + Account const bob{"bob"}; env.fund(XRP(10000), alice, bob); env.trust(alice["USD"](1000), bob); for (auto i = 0; i < 20; ++i) diff --git a/src/test/rpc/Handler_test.cpp b/src/test/rpc/Handler_test.cpp index 7aeb059f56..30ea8831ff 100644 --- a/src/test/rpc/Handler_test.cpp +++ b/src/test/rpc/Handler_test.cpp @@ -25,7 +25,7 @@ operator<<(std::ostream& os, std::chrono::nanoseconds ns) // NOTE This is a rather naive effort at a microbenchmark. Ideally we want // Google Benchmark, or something similar. Also, this actually does not belong // to unit tests, as it makes little sense to run it in conditions very -// dissimilar to how rippled will normally work. +// dissimilar to how xrpld will normally work. // TODO as https://github.com/XRPLF/rippled/issues/4765 class Handler_test : public beast::unit_test::suite diff --git a/src/test/rpc/JSONRPC_test.cpp b/src/test/rpc/JSONRPC_test.cpp index 87c7d3dcd0..b9d4ee4a98 100644 --- a/src/test/rpc/JSONRPC_test.cpp +++ b/src/test/rpc/JSONRPC_test.cpp @@ -2119,7 +2119,7 @@ public: jt.jv.removeMember(jss::Fee); jt.jv.removeMember(jss::TxnSignature); req[jss::tx_json] = jt.jv; - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2189,7 +2189,7 @@ public: alice)); req[jss::tx_json] = jt.jv; - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2216,7 +2216,7 @@ public: { Json::Value req; Json::Reader().parse("{ \"fee_mult_max\" : 1, \"tx_json\" : { } } ", req); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2236,7 +2236,7 @@ public: "{ \"fee_mult_max\" : 3, \"fee_div_max\" : 2, " "\"tx_json\" : { } } ", req); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2253,7 +2253,7 @@ public: { Json::Value req; Json::Reader().parse("{ \"fee_mult_max\" : 0, \"tx_json\" : { } } ", req); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2274,7 +2274,7 @@ public: "{ \"fee_mult_max\" : 3, \"fee_div_max\" : 6, " "\"tx_json\" : { } } ", req); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2293,7 +2293,7 @@ public: "{ \"fee_mult_max\" : 0, \"fee_div_max\" : 2, " "\"tx_json\" : { } } ", req); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2312,7 +2312,7 @@ public: "{ \"fee_mult_max\" : 10, \"fee_div_max\" : 0, " "\"tx_json\" : { } } ", req); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2330,7 +2330,7 @@ public: Json::Value req; test::jtx::Account const alice("alice"); req[jss::tx_json] = test::jtx::acctdelete(env.master.human(), alice.human()); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2367,7 +2367,7 @@ public: "tx_json" : { } })", req); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2389,7 +2389,7 @@ public: "tx_json" : { } })", req); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2417,7 +2417,7 @@ public: "tx_json" : { } })", req); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2440,7 +2440,7 @@ public: "tx_json" : { } })", req); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2463,7 +2463,7 @@ public: "tx_json" : { } })", req); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2486,7 +2486,7 @@ public: "tx_json" : { } })", req); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2509,7 +2509,7 @@ public: "tx_json" : { } })", req); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2530,7 +2530,7 @@ public: "tx_json" : { } })", req); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2552,7 +2552,7 @@ public: "tx_json" : { } })", req); - Json::Value result = checkFee( + Json::Value const result = checkFee( req, Role::ADMIN, true, @@ -2648,7 +2648,7 @@ public: env(noop(env.master), fee(47)); } - Env_ss envs(env); + Env_ss const envs(env); Json::Value toSign; toSign[jss::tx_json] = noop(env.master); @@ -2732,7 +2732,7 @@ public: env(pay(g, env.master, USD(50))); env.close(); - ProcessTransactionFn processTxn = fakeProcessTransaction; + ProcessTransactionFn const processTxn = fakeProcessTransaction; // A list of all the functions we want to test. using signFunc = Json::Value (*)( @@ -2773,7 +2773,7 @@ public: static Role const testedRoles[] = { Role::GUEST, Role::USER, Role::ADMIN, Role::FORBID}; - for (Role testRole : testedRoles) + for (Role const testRole : testedRoles) { Json::Value result; auto const signFn = get<0>(testFunc); diff --git a/src/test/rpc/KeyGeneration_test.cpp b/src/test/rpc/KeyGeneration_test.cpp index 3bffb9dc83..6a6d71ca10 100644 --- a/src/test/rpc/KeyGeneration_test.cpp +++ b/src/test/rpc/KeyGeneration_test.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include @@ -98,7 +98,7 @@ public: params.isMember(jss::key_type) ? params[jss::key_type] : "secp256k1"); BEAST_EXPECT(!result.isMember(jss::warning)); - std::string seed = result[jss::master_seed].asString(); + std::string const seed = result[jss::master_seed].asString(); result = walletPropose(params); @@ -164,9 +164,13 @@ public: auto const wallet = testSecretWallet(params, strings); if (value == strings.passphrase) + { BEAST_EXPECT(wallet[jss::warning] == strings.passphrase_warning); + } else + { BEAST_EXPECT(!wallet.isMember(jss::warning)); + } } void @@ -294,7 +298,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(ret->first == publicKey); @@ -308,7 +312,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(ret->first == publicKey); @@ -322,7 +326,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(ret->first == publicKey); @@ -341,7 +345,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(ret->first == publicKey); @@ -357,7 +361,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(ret->first == publicKey); @@ -373,7 +377,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(ret->first == publicKey); @@ -681,9 +685,9 @@ public: } void - testRippleLibEd25519() + testXrplLibEd25519() { - testcase("ripple-lib encoded Ed25519 keys"); + testcase("XrplLib encoded Ed25519 keys"); auto test = [this](char const* seed, char const* addr) { { @@ -695,7 +699,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(toBase58(calcAccountID(ret->first)) == addr); @@ -726,7 +730,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(toBase58(calcAccountID(ret->first)) == addr); @@ -780,7 +784,7 @@ public: testKeypairForSignature(std::string("ed25519"), ed25519_strings); testKeypairForSignature(std::string("secp256k1"), strong_brain_strings); - testRippleLibEd25519(); + testXrplLibEd25519(); testKeypairForSignatureErrors(); } diff --git a/src/test/rpc/LedgerData_test.cpp b/src/test/rpc/LedgerData_test.cpp index e139ba24b4..472f5b2aa2 100644 --- a/src/test/rpc/LedgerData_test.cpp +++ b/src/test/rpc/LedgerData_test.cpp @@ -13,7 +13,7 @@ public: checkMarker(Json::Value const& val) { return val.isMember(jss::marker) && val[jss::marker].isString() && - val[jss::marker].asString().size() > 0; + !val[jss::marker].asString().empty(); } void @@ -198,8 +198,10 @@ public: jvParams[jss::ledger_index] = "closed"; auto jrr = env.rpc("json", "ledger_data", to_string(jvParams))[jss::result]; if (BEAST_EXPECT(jrr.isMember(jss::ledger))) + { BEAST_EXPECT( jrr[jss::ledger][jss::ledger_hash] == to_string(env.closed()->header().hash)); + } } { // Closed ledger with binary form @@ -210,7 +212,7 @@ public: if (BEAST_EXPECT(jrr.isMember(jss::ledger))) { auto data = strUnHex(jrr[jss::ledger][jss::ledger_data].asString()); - if (BEAST_EXPECT(data)) + if (BEAST_EXPECT(data); data.has_value()) { Serializer s(data->data(), data->size()); std::uint32_t seq = 0; diff --git a/src/test/rpc/LedgerEntry_test.cpp b/src/test/rpc/LedgerEntry_test.cpp index ffc4329109..bd053d71c9 100644 --- a/src/test/rpc/LedgerEntry_test.cpp +++ b/src/test/rpc/LedgerEntry_test.cpp @@ -60,10 +60,8 @@ getFieldType(Json::StaticString fieldName) { return it->second; } - else - { - Throw("`mappings` is missing field " + std::string(fieldName.c_str())); - } + + Throw("`mappings` is missing field " + std::string(fieldName.c_str())); } std::string @@ -110,10 +108,12 @@ class LedgerEntry_test : public beast::unit_test::suite if (BEAST_EXPECT(jv.isMember(jss::status))) BEAST_EXPECTS(jv[jss::status] == "error", std::to_string(location.line())); if (BEAST_EXPECT(jv.isMember(jss::error))) + { BEAST_EXPECTS( jv[jss::error] == err, "Expected error " + err + ", received " + jv[jss::error].asString() + ", at line " + std::to_string(location.line()) + ", " + jv.toStyledString()); + } if (msg.empty()) { BEAST_EXPECTS( @@ -122,14 +122,16 @@ class LedgerEntry_test : public beast::unit_test::suite "\", at line " + std::to_string(location.line()) + ", " + jv.toStyledString()); } else if (BEAST_EXPECT(jv.isMember(jss::error_message))) + { BEAST_EXPECTS( jv[jss::error_message] == msg, "Expected error message \"" + msg + "\", received \"" + jv[jss::error_message].asString() + "\", at line " + std::to_string(location.line()) + ", " + jv.toStyledString()); + } } - std::vector + static std::vector getBadValues(FieldType fieldType) { static Json::Value const injectObject = []() { @@ -170,12 +172,12 @@ class LedgerEntry_test : public beast::unit_test::suite }; auto remove = [&](std::vector indices) -> std::vector { - std::unordered_set indexSet(indices.begin(), indices.end()); + std::unordered_set const indexSet(indices.begin(), indices.end()); std::vector values; values.reserve(allBadValues.size() - indexSet.size()); for (std::size_t i = 0; i < allBadValues.size(); ++i) { - if (indexSet.find(i) == indexSet.end()) + if (!indexSet.contains(i)) { values.push_back(allBadValues[i]); } @@ -223,7 +225,7 @@ class LedgerEntry_test : public beast::unit_test::suite } } - Json::Value + static Json::Value getCorrectValue(Json::StaticString fieldName) { static Json::Value const twoAccountArray = []() { @@ -286,10 +288,14 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value const jrr = env.rpc( apiVersion, "json", "ledger_entry", to_string(correctRequest))[jss::result]; if (apiVersion < 2u) + { checkErrorValue(jrr, "unknownOption", "", location); + } else + { checkErrorValue( jrr, "invalidParams", "No ledger_entry params provided.", location); + } } auto tryField = [&](Json::Value fieldValue) -> void { correctRequest[fieldName] = fieldValue; @@ -491,9 +497,13 @@ class LedgerEntry_test : public beast::unit_test::suite env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; if (apiVersion < 2u) + { checkErrorValue(jrr, "unknownOption", ""); + } else + { checkErrorValue(jrr, "invalidParams", "No ledger_entry params provided."); + } } }); } @@ -585,7 +595,7 @@ class LedgerEntry_test : public beast::unit_test::suite } { // Check malformed cases - Json::Value jvParams; + Json::Value const jvParams; testMalformedField( env, jvParams, jss::account_root, FieldType::AccountField, "malformedAddress"); } @@ -663,7 +673,7 @@ class LedgerEntry_test : public beast::unit_test::suite view.rawInsert(sle); return true; }; - env.app().openLedger().modify(amendments); + env.app().getOpenLedger().modify(amendments); } Json::Value jvParams; @@ -689,7 +699,7 @@ class LedgerEntry_test : public beast::unit_test::suite Account const alice{"alice"}; env.fund(XRP(10000), alice); env.close(); - AMM amm(env, alice, XRP(10), alice["USD"](1000)); + AMM const amm(env, alice, XRP(10), alice["USD"](1000)); env.close(); { @@ -1551,7 +1561,7 @@ class LedgerEntry_test : public beast::unit_test::suite view.rawInsert(sle); return true; }; - env.app().openLedger().modify(nUNL); + env.app().getOpenLedger().modify(nUNL); } Json::Value jvParams; @@ -1951,7 +1961,7 @@ class LedgerEntry_test : public beast::unit_test::suite } { // Malformed DID index - Json::Value jvParams; + Json::Value const jvParams; testMalformedField( env, jvParams, jss::did, FieldType::AccountField, "malformedAddress"); } @@ -1967,7 +1977,7 @@ class LedgerEntry_test : public beast::unit_test::suite Env env(*this); Account const owner("owner"); env.fund(XRP(1'000), owner); - Oracle oracle( + Oracle const oracle( env, {.owner = owner, .fee = static_cast(env.current()->fees().base.drops())}); { @@ -1998,11 +2008,11 @@ class LedgerEntry_test : public beast::unit_test::suite Account const owner(std::string("owner") + std::to_string(i)); env.fund(XRP(1'000), owner); // different accounts can have the same asset pair - Oracle oracle(env, {.owner = owner, .documentID = i, .fee = baseFee}); + Oracle const oracle(env, {.owner = owner, .documentID = i, .fee = baseFee}); accounts.push_back(owner.id()); oracles.push_back(oracle.documentID()); // same account can have different asset pair - Oracle oracle1(env, {.owner = owner, .documentID = i + 10, .fee = baseFee}); + Oracle const oracle1(env, {.owner = owner, .documentID = i + 10, .fee = baseFee}); accounts.push_back(owner.id()); oracles.push_back(oracle1.documentID()); } @@ -2092,7 +2102,7 @@ class LedgerEntry_test : public beast::unit_test::suite } { // Malformed MPTIssuance index - Json::Value jvParams; + Json::Value const jvParams; testMalformedField( env, jvParams, jss::mptoken, FieldType::HashOrObjectField, "malformedRequest"); } @@ -2179,7 +2189,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->START_UP = StartUpType::Fresh; return cfg; })}; @@ -2372,7 +2382,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->START_UP = StartUpType::Fresh; return cfg; })}; @@ -2640,7 +2650,9 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, BEAST_EXPECT(jv[jss::error_message] == Json::nullValue || jv[jss::error_message] == ""); } else if (BEAST_EXPECT(jv.isMember(jss::error_message))) + { BEAST_EXPECT(jv[jss::error_message] == msg); + } } void diff --git a/src/test/rpc/LedgerRPC_test.cpp b/src/test/rpc/LedgerRPC_test.cpp index d45f50c657..8f965aa2cf 100644 --- a/src/test/rpc/LedgerRPC_test.cpp +++ b/src/test/rpc/LedgerRPC_test.cpp @@ -30,15 +30,17 @@ class LedgerRPC_test : public beast::unit_test::suite BEAST_EXPECT(jv[jss::error_message] == Json::nullValue || jv[jss::error_message] == ""); } else if (BEAST_EXPECT(jv.isMember(jss::error_message))) + { BEAST_EXPECTS( jv[jss::error_message] == msg, "Expected error message \"" + msg + "\", received \"" + jv[jss::error_message].asString() + "\""); + } } // Corrupt a valid address by replacing the 10th character with '!'. // '!' is not part of the ripple alphabet. - std::string + static std::string makeBadAddress(std::string good) { std::string ret = std::move(good); @@ -634,15 +636,19 @@ class LedgerRPC_test : public beast::unit_test::suite BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray()); for (auto i = 0; i < jrr[jss::ledger][jss::accountState].size(); i++) + { if (jrr[jss::ledger][jss::accountState][i]["LedgerEntryType"] == jss::LedgerHashes) { index = jrr[jss::ledger][jss::accountState][i]["index"].asString(); hashesLedgerEntryIndex = i; } + } for (auto const& object : jrr[jss::ledger][jss::accountState]) + { if (object["LedgerEntryType"] == jss::LedgerHashes) index = object["index"].asString(); + } // jss::type is a deprecated field BEAST_EXPECT( diff --git a/src/test/rpc/LedgerRequest_test.cpp b/src/test/rpc/LedgerRequest_test.cpp index 18c717ec19..4462c1f039 100644 --- a/src/test/rpc/LedgerRequest_test.cpp +++ b/src/test/rpc/LedgerRequest_test.cpp @@ -102,7 +102,7 @@ public: } { - std::string ledgerHash(64, 'q'); + std::string const ledgerHash(64, 'q'); auto const result = env.rpc("ledger_request", ledgerHash); @@ -113,7 +113,7 @@ public: } { - std::string ledgerHash(64, '1'); + std::string const ledgerHash(64, '1'); auto const result = env.rpc("ledger_request", ledgerHash); diff --git a/src/test/rpc/NoRippleCheck_test.cpp b/src/test/rpc/NoRippleCheck_test.cpp index c436c1d7b1..54a2931ba5 100644 --- a/src/test/rpc/NoRippleCheck_test.cpp +++ b/src/test/rpc/NoRippleCheck_test.cpp @@ -235,8 +235,10 @@ public: { testBadInput(); for (auto user : {true, false}) + { for (auto problem : {true, false}) testBasic(user, problem); + } } }; diff --git a/src/test/rpc/NoRipple_test.cpp b/src/test/rpc/NoRipple_test.cpp index 27f13aa3e2..fcf412bbd4 100644 --- a/src/test/rpc/NoRipple_test.cpp +++ b/src/test/rpc/NoRipple_test.cpp @@ -234,9 +234,13 @@ public: auto lines = env.rpc("json", "noripple_check", to_string(params)); if (apiVersion < 2u) + { BEAST_EXPECT(lines[jss::result][jss::status] == "success"); + } else + { BEAST_EXPECT(lines[jss::result][jss::error] == "invalidParams"); + } } } } diff --git a/src/test/rpc/Peers_test.cpp b/src/test/rpc/Peers_test.cpp index d72ad6d878..984e767516 100644 --- a/src/test/rpc/Peers_test.cpp +++ b/src/test/rpc/Peers_test.cpp @@ -31,10 +31,10 @@ class Peers_test : public beast::unit_test::suite { auto kp = generateKeyPair(KeyType::secp256k1, generateSeed("seed" + std::to_string(i))); - std::string name = "Node " + std::to_string(i); + std::string const name = "Node " + std::to_string(i); using namespace std::chrono_literals; - env.app().cluster().update(kp.first, name, 200, env.timeKeeper().now() - 10s); + env.app().getCluster().update(kp.first, name, 200, env.timeKeeper().now() - 10s); nodes.insert(std::make_pair(toBase58(TokenType::NodePublic, kp.first), name)); } diff --git a/src/test/rpc/RPCCall_test.cpp b/src/test/rpc/RPCCall_test.cpp index e4ef3aca6a..ae876dded4 100644 --- a/src/test/rpc/RPCCall_test.cpp +++ b/src/test/rpc/RPCCall_test.cpp @@ -5854,7 +5854,7 @@ public: apiVersion <= RPC::apiMaximumValidVersion)) return; - test::jtx::Env env(*this, makeNetworkConfig(11111)); // Used only for its Journal. + test::jtx::Env const env(*this, makeNetworkConfig(11111)); // Used only for its Journal. // For each RPCCall test. for (RPCCallTestData const& rpcCallTest : rpcCallTestArray) diff --git a/src/test/rpc/RPCOverload_test.cpp b/src/test/rpc/RPCOverload_test.cpp index c8f02cd0a3..e5558af82c 100644 --- a/src/test/rpc/RPCOverload_test.cpp +++ b/src/test/rpc/RPCOverload_test.cpp @@ -45,7 +45,9 @@ public: jv = jv[jss::result]; // When booted, we just get a null json response if (jv.isNull()) + { booted = true; + } else if (!(jv.isMember(jss::status) && (jv[jss::status] == "success"))) { // Don't use BEAST_EXPECT above b/c it will be called a diff --git a/src/test/rpc/Roles_test.cpp b/src/test/rpc/Roles_test.cpp index a4f9de2284..314d0972d2 100644 --- a/src/test/rpc/Roles_test.cpp +++ b/src/test/rpc/Roles_test.cpp @@ -12,7 +12,7 @@ namespace test { class Roles_test : public beast::unit_test::suite { - bool + static bool isValidIpAddress(std::string const& addr) { boost::system::error_code ec; @@ -43,6 +43,7 @@ class Roles_test : public beast::unit_test::suite Env env{*this, envconfig(secure_gateway)}; BEAST_EXPECT(env.rpc("ping")["result"]["role"] == "proxied"); + BEAST_EXPECT(!env.rpc("ping")["result"].isMember("ip")); auto wsRes = makeWSClient(env.app().config())->invoke("ping")["result"]; BEAST_EXPECT(!wsRes.isMember("unlimited") || !wsRes["unlimited"].asBool()); diff --git a/src/test/rpc/ServerInfo_test.cpp b/src/test/rpc/ServerInfo_test.cpp index 9fdfe5e13b..495bb8ba1b 100644 --- a/src/test/rpc/ServerInfo_test.cpp +++ b/src/test/rpc/ServerInfo_test.cpp @@ -33,7 +33,7 @@ public: makeValidatorConfig() { auto p = std::make_unique(); - boost::format toLoad(R"rippleConfig( + boost::format toLoad(R"xrpldConfig( [validator_token] %1% @@ -49,7 +49,7 @@ ip = 0.0.0.0 port = 50052 protocol = wss2 admin = 127.0.0.1 -)rippleConfig"); +)xrpldConfig"); p->loadFromString(boost::str(toLoad % validator_data::token % validator_data::public_key)); @@ -85,7 +85,7 @@ admin = 127.0.0.1 (git[jss::hash].isString() && git[jss::hash].asString().size() == 40)); BEAST_EXPECT( !git.isMember(jss::branch) || - (git[jss::branch].isString() && git[jss::branch].asString().size() != 0)); + (git[jss::branch].isString() && !git[jss::branch].asString().empty())); } } @@ -93,7 +93,7 @@ admin = 127.0.0.1 Env env(*this); // Call NetworkOPs directly and set the admin flag to false. - auto const result = env.app().getOPs().getServerInfo(true, false, 0); + auto const result = env.app().getOPs().getServerInfo(true, false, false); // Expect that the admin ports are not included in the result. auto const& ports = result[jss::ports]; BEAST_EXPECT(ports.isArray() && ports.size() == 0); diff --git a/src/test/rpc/Simulate_test.cpp b/src/test/rpc/Simulate_test.cpp index d1b1cd02fc..0581313e7a 100644 --- a/src/test/rpc/Simulate_test.cpp +++ b/src/test/rpc/Simulate_test.cpp @@ -41,7 +41,7 @@ class Simulate_test : public beast::unit_test::suite else { auto const unHexed = strUnHex(result[jss::tx_blob].asString()); - SerialIter sitTrans(makeSlice(*unHexed)); + SerialIter sitTrans(makeSlice(*unHexed)); // NOLINT(bugprone-unchecked-optional-access) tx_json = STObject(std::ref(sitTrans), sfGeneric).getJson(JsonOptions::none); } BEAST_EXPECT(tx_json[jss::TransactionType] == tx[jss::TransactionType]); @@ -59,7 +59,7 @@ class Simulate_test : public beast::unit_test::suite int const expectedSequence, XRPAmount const& expectedFee) { - return checkBasicReturnValidity( + checkBasicReturnValidity( result, tx, expectedSequence, expectedFee.jsonClipped().asString()); } @@ -87,6 +87,7 @@ class Simulate_test : public beast::unit_test::suite // It is technically not a valid STObject, so the following line // will crash STParsedJSONObject const parsed(std::string(jss::tx_json), tx); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const tx_blob = strHex(parsed.object->getSerializer().peekData()); if (BEAST_EXPECT(parsed.object.has_value())) { @@ -130,13 +131,13 @@ class Simulate_test : public beast::unit_test::suite BEAST_EXPECTS(env.current()->txCount() == 0, std::to_string(env.current()->txCount())); } - Json::Value - getJsonMetadata(Json::Value txResult) const + static Json::Value + getJsonMetadata(Json::Value txResult) { if (txResult.isMember(jss::meta_blob)) { auto unHexed = strUnHex(txResult[jss::meta_blob].asString()); - SerialIter sitTrans(makeSlice(*unHexed)); + SerialIter sitTrans(makeSlice(*unHexed)); // NOLINT(bugprone-unchecked-optional-access) return STObject(std::ref(sitTrans), sfGeneric).getJson(JsonOptions::none); } @@ -1126,7 +1127,7 @@ class Simulate_test : public beast::unit_test::suite tx[jss::TransactionType] = jss::NFTokenMint; tx[sfNFTokenTaxon] = 1; - Json::Value nftokenId = to_string(token::getNextID(env, alice, 1)); + Json::Value const nftokenId = to_string(token::getNextID(env, alice, 1)); // test nft synthetic testTxJsonMetadataField(env, tx, validateOutput, jss::nftoken_id, nftokenId); } @@ -1136,7 +1137,7 @@ class Simulate_test : public beast::unit_test::suite tx[jss::Account] = alice.human(); tx[jss::TransactionType] = jss::MPTokenIssuanceCreate; - Json::Value mptIssuanceId = to_string(makeMptID(env.seq(alice), alice)); + Json::Value const mptIssuanceId = to_string(makeMptID(env.seq(alice), alice)); // test mpt issuance id testTxJsonMetadataField( env, tx, validateOutput, jss::mpt_issuance_id, mptIssuanceId); diff --git a/src/test/rpc/Status_test.cpp b/src/test/rpc/Status_test.cpp index 01fc81430f..a4a7b8c961 100644 --- a/src/test/rpc/Status_test.cpp +++ b/src/test/rpc/Status_test.cpp @@ -136,7 +136,8 @@ private: expect(m == message, m + " != " + message); auto d = error[jss::data]; - size_t s1 = d.size(), s2 = messages.size(); + size_t const s1 = d.size(); + size_t const s2 = messages.size(); expect( s1 == s2, prefix + "Data sizes differ " + std::to_string(s1) + " != " + std::to_string(s2)); diff --git a/src/test/rpc/Subscribe_test.cpp b/src/test/rpc/Subscribe_test.cpp index 414bceefd7..bf52bde7aa 100644 --- a/src/test/rpc/Subscribe_test.cpp +++ b/src/test/rpc/Subscribe_test.cpp @@ -400,7 +400,7 @@ public: if (!BEAST_EXPECT(cfg.section(SECTION_VALIDATION_SEED).empty())) return; auto const parsedseed = parseBase58(cfg.section(SECTION_VALIDATION_SEED).values()[0]); - if (!BEAST_EXPECT(parsedseed)) + if (BEAST_EXPECT(parsedseed); not parsedseed.has_value()) return; std::string const valPublicKey = toBase58( @@ -461,7 +461,7 @@ public: if (!jv.isMember(jss::validated_hash)) return false; - uint32_t netID = env.app().getNetworkIDService().getNetworkID(); + uint32_t const netID = env.app().getNetworkIDService().getNetworkID(); if (!jv.isMember(jss::network_id) || jv[jss::network_id] != netID) return false; @@ -784,9 +784,9 @@ public: using IdxHashVec = std::vector>; Account alice("alice"); - Account bob("bob"); + Account const bob("bob"); Account carol("carol"); - Account david("david"); + Account const david("david"); /////////////////////////////////////////////////////////////////// /* @@ -802,13 +802,17 @@ public: * return {true, true} if received numReplies replies and also * received a tx with the account_history_tx_first == true */ - auto getTxHash = [](WSClient& wsc, IdxHashVec& v, int numReplies) -> std::pair { + auto getTxHash = [](WSClient& wsc, + IdxHashVec& v, + int numReplies, + std::chrono::milliseconds timeout = + std::chrono::milliseconds{5000}) -> std::pair { bool first_flag = false; for (int i = 0; i < numReplies; ++i) { std::uint32_t idx{0}; - auto reply = wsc.getMsg(100ms); + auto reply = wsc.getMsg(timeout); if (reply) { auto r = *reply; @@ -816,8 +820,8 @@ public: idx = r[jss::account_history_tx_index].asInt(); if (r.isMember(jss::account_history_tx_first)) first_flag = true; - bool boundary = r.isMember(jss::account_history_boundary); - int ledger_idx = r[jss::ledger_index].asInt(); + bool const boundary = r.isMember(jss::account_history_boundary); + int const ledger_idx = r[jss::ledger_index].asInt(); if (r.isMember(jss::transaction) && r[jss::transaction].isMember(jss::hash)) { auto t{r[jss::transaction]}; @@ -928,7 +932,7 @@ public: // (-10, "E5B8B...", true, 4 auto checkBoundary = [](IdxHashVec const& vec, bool /* forward */) { - size_t num_tx = vec.size(); + size_t const num_tx = vec.size(); for (size_t i = 0; i < num_tx; ++i) { auto [idx, hash, boundary, ledger] = vec[i]; @@ -982,7 +986,7 @@ public: BEAST_EXPECT(goodSubRPC(jv)); sendPayments(env, env.master, alice, 1, 1); - r = getTxHash(*wscTxHistory, vec, 1); + r = getTxHash(*wscTxHistory, vec, 1, 10ms); BEAST_EXPECT(!r.first); } { @@ -1001,7 +1005,7 @@ public: return; IdxHashVec genesisFullHistoryVec; BEAST_EXPECT(env.syncClose()); - if (!BEAST_EXPECT(!getTxHash(*wscTxHistory, genesisFullHistoryVec, 1).first)) + if (!BEAST_EXPECT(!getTxHash(*wscTxHistory, genesisFullHistoryVec, 1, 10ms).first)) return; /* @@ -1071,7 +1075,7 @@ public: auto wscAccount = makeWSClient(env.app().config()); auto wscTxHistory = makeWSClient(env.app().config()); - std::array accounts = {alice, bob}; + std::array const accounts = {alice, bob}; env.fund(XRP(222222), accounts); BEAST_EXPECT(env.syncClose()); @@ -1139,7 +1143,7 @@ public: Env env(*this, single_thread_io(envconfig())); auto const USD_a = alice["USD"]; - std::array accounts = {alice, carol}; + std::array const accounts = {alice, carol}; env.fund(XRP(333333), accounts); env.trust(USD_a(20000), carol); BEAST_EXPECT(env.syncClose()); @@ -1161,7 +1165,7 @@ public: { // take out existing txns from the stream IdxHashVec tempVec; - getTxHash(*ws, tempVec, 100); + getTxHash(*ws, tempVec, 100, 1000ms); } auto count = mixedPayments(); @@ -1176,7 +1180,7 @@ public: * long transaction history */ Env env(*this, single_thread_io(envconfig())); - std::array accounts = {alice, carol}; + std::array const accounts = {alice, carol}; env.fund(XRP(444444), accounts); BEAST_EXPECT(env.syncClose()); @@ -1195,7 +1199,7 @@ public: { // take out existing txns from the stream IdxHashVec tempVec; - getTxHash(*wscLong, tempVec, 100); + getTxHash(*wscLong, tempVec, 100, 1000ms); } // repeat the payments many rounds @@ -1230,7 +1234,7 @@ public: featurePermissionedDEX}; Env env(*this, single_thread_io(envconfig()), all); - PermissionedDEX permDex(env); + PermissionedDEX const permDex(env); auto const alice = permDex.alice; auto const bob = permDex.bob; auto const carol = permDex.carol; diff --git a/src/test/rpc/TransactionEntry_test.cpp b/src/test/rpc/TransactionEntry_test.cpp index 14e75d04da..6421587478 100644 --- a/src/test/rpc/TransactionEntry_test.cpp +++ b/src/test/rpc/TransactionEntry_test.cpp @@ -211,8 +211,8 @@ class TransactionEntry_test : public beast::unit_test::suite BEAST_EXPECT(clHash["result"] == resIndex); }; - Account A1{"A1"}; - Account A2{"A2"}; + Account const A1{"A1"}; + Account const A2{"A2"}; env.fund(XRP(10000), A1); auto fund_1_tx = to_string(env.tx()->getTransactionID()); diff --git a/src/test/rpc/Transaction_test.cpp b/src/test/rpc/Transaction_test.cpp index ebe3f3135d..9f06607729 100644 --- a/src/test/rpc/Transaction_test.cpp +++ b/src/test/rpc/Transaction_test.cpp @@ -19,7 +19,7 @@ namespace xrpl { class Transaction_test : public beast::unit_test::suite { - std::unique_ptr + static std::unique_ptr makeNetworkConfig(uint32_t networkID) { using namespace test::jtx; @@ -91,10 +91,14 @@ class Transaction_test : public beast::unit_test::suite result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == NOT_FOUND); - if (deltaEndSeq) + if (deltaEndSeq != 0) + { BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool()); + } else + { BEAST_EXPECT(result[jss::result][jss::searched_all].asBool()); + } } // Find transactions outside of provided range. @@ -279,7 +283,7 @@ class Transaction_test : public beast::unit_test::suite char const* EXCESSIVE = RPC::get_error_info(rpcEXCESSIVE_LGR_RANGE).token; Env env{*this, makeNetworkConfig(11111)}; - uint32_t netID = env.app().getNetworkIDService().getNetworkID(); + uint32_t const netID = env.app().getNetworkIDService().getNetworkID(); auto const alice = Account("alice"); env.fund(XRP(1000), alice); @@ -302,9 +306,10 @@ class Transaction_test : public beast::unit_test::suite { auto const& tx = txns[i]; auto const& meta = metas[i]; - uint32_t txnIdx = meta->getFieldU32(sfTransactionIndex); + uint32_t const txnIdx = meta->getFieldU32(sfTransactionIndex); auto const result = env.rpc( COMMAND, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) *RPC::encodeCTID(startLegSeq + i, txnIdx, netID), BINARY, to_string(startLegSeq), @@ -316,6 +321,7 @@ class Transaction_test : public beast::unit_test::suite } auto const tx = env.jt(noop(alice), seq(env.seq(alice))).stx; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const ctid = *RPC::encodeCTID(endLegSeq, tx->getSeqValue(), netID); for (int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq) { @@ -326,10 +332,14 @@ class Transaction_test : public beast::unit_test::suite result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == NOT_FOUND); - if (deltaEndSeq) + if (deltaEndSeq != 0) + { BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool()); + } else + { BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool()); + } } // Find transactions outside of provided range. @@ -337,9 +347,10 @@ class Transaction_test : public beast::unit_test::suite { // auto const& tx = txns[i]; auto const& meta = metas[i]; - uint32_t txnIdx = meta->getFieldU32(sfTransactionIndex); + uint32_t const txnIdx = meta->getFieldU32(sfTransactionIndex); auto const result = env.rpc( COMMAND, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) *RPC::encodeCTID(startLegSeq + i, txnIdx, netID), BINARY, to_string(endLegSeq + 1), @@ -396,9 +407,10 @@ class Transaction_test : public beast::unit_test::suite // field. (Tests parameter parsing) { auto const& meta = metas[0]; - uint32_t txnIdx = meta->getFieldU32(sfTransactionIndex); + uint32_t const txnIdx = meta->getFieldU32(sfTransactionIndex); auto const result = env.rpc( COMMAND, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) *RPC::encodeCTID(endLegSeq, txnIdx, netID), to_string(startLegSeq), to_string(deletedLedger - 1)); @@ -488,7 +500,7 @@ class Transaction_test : public beast::unit_test::suite using namespace test::jtx; using std::to_string; - Env env{*this, makeNetworkConfig(11111)}; + Env const env{*this, makeNetworkConfig(11111)}; // Test case 1: Valid input values auto const expected11 = std::optional("CFFFFFFFFFFFFFFF"); @@ -571,7 +583,7 @@ class Transaction_test : public beast::unit_test::suite using namespace test::jtx; // Use a Concise Transaction Identifier to request a transaction. - for (uint32_t netID : {11111, 65535, 65536}) + for (uint32_t const netID : {11111, 65535, 65536}) { Env env{*this, makeNetworkConfig(netID)}; BEAST_EXPECT(netID == env.app().getNetworkIDService().getNetworkID()); @@ -594,7 +606,7 @@ class Transaction_test : public beast::unit_test::suite Json::Value jsonTx; jsonTx[jss::binary] = false; - jsonTx[jss::ctid] = *ctid; + jsonTx[jss::ctid] = *ctid; // NOLINT(bugprone-unchecked-optional-access) jsonTx[jss::id] = 1; auto const jrr = env.rpc("json", "tx", to_string(jsonTx))[jss::result]; BEAST_EXPECT(jrr[jss::ctid] == ctid); @@ -614,6 +626,7 @@ class Transaction_test : public beast::unit_test::suite env(pay(alice, bob, XRP(10))); env.close(); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) std::string const ctid = *RPC::encodeCTID(startLegSeq, 0, netID); auto isUpper = [](char c) { return std::isupper(c) != 0; }; @@ -642,7 +655,7 @@ class Transaction_test : public beast::unit_test::suite // test that if the network is 65535 the ctid is not in the response // Using a hash to request the transaction, test the network ID // boundary where the CTID is (not) in the response. - for (uint32_t netID : {2, 1024, 65535, 65536}) + for (uint32_t const netID : {2, 1024, 65535, 65536}) { Env env{*this, makeNetworkConfig(netID)}; BEAST_EXPECT(netID == env.app().getNetworkIDService().getNetworkID()); @@ -670,14 +683,15 @@ class Transaction_test : public beast::unit_test::suite if (jrr.isMember(jss::ctid)) { auto const ctid = RPC::encodeCTID(ledgerSeq, 0, netID); - BEAST_EXPECT(jrr[jss::ctid] == *ctid); + BEAST_EXPECT( + jrr[jss::ctid] == *ctid); // NOLINT(bugprone-unchecked-optional-access) } } // test the wrong network ID was submitted { Env env{*this, makeNetworkConfig(21337)}; - uint32_t netID = env.app().getNetworkIDService().getNetworkID(); + uint32_t const netID = env.app().getNetworkIDService().getNetworkID(); auto const alice = Account("alice"); auto const bob = Account("bob"); @@ -687,6 +701,7 @@ class Transaction_test : public beast::unit_test::suite env(pay(alice, bob, XRP(10))); env.close(); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const ctid = *RPC::encodeCTID(startLegSeq, 0, netID + 1); Json::Value jsonTx; jsonTx[jss::binary] = false; @@ -728,9 +743,9 @@ class Transaction_test : public beast::unit_test::suite // Payment env(pay(alice, gw, XRP(100))); - std::shared_ptr txn = env.tx(); + std::shared_ptr const txn = env.tx(); env.close(); - std::shared_ptr meta = + std::shared_ptr const meta = env.closed()->txRead(env.tx()->getTransactionID()).second; Json::Value expected = txn->getJson(JsonOptions::none); @@ -802,7 +817,8 @@ class Transaction_test : public beast::unit_test::suite to_string(txn->getTransactionID()) == "3F8BDE5A5F82C4F4708E5E9255B713E303E6E1A371FD5C7A704AFD1387C23981"); env.close(); - std::shared_ptr meta = env.closed()->txRead(txn->getTransactionID()).second; + std::shared_ptr const meta = + env.closed()->txRead(txn->getTransactionID()).second; std::string const expected_tx_blob = serializeHex(*txn); std::string const expected_meta_blob = serializeHex(*meta); diff --git a/src/test/rpc/ValidatorRPC_test.cpp b/src/test/rpc/ValidatorRPC_test.cpp index 829e2699a7..6c6a75dd01 100644 --- a/src/test/rpc/ValidatorRPC_test.cpp +++ b/src/test/rpc/ValidatorRPC_test.cpp @@ -27,7 +27,7 @@ public: for (bool const isAdmin : {true, false}) { - for (std::string cmd : {"validators", "validator_list_sites"}) + for (std::string const cmd : {"validators", "validator_list_sites"}) { Env env{*this, isAdmin ? envconfig() : envconfig(no_admin)}; env.set_retries(isAdmin ? 5 : 0); @@ -122,7 +122,7 @@ public: auto k2 = randomKeyPair(KeyType::ed25519).first; disabledKeys.insert(k1); disabledKeys.insert(k2); - env.app().validators().setNegativeUNL(disabledKeys); + env.app().getValidators().setNegativeUNL(disabledKeys); auto const jrr = env.rpc("validators")[jss::result]; auto& jrrnUnl = jrr[jss::NegativeUNL]; @@ -134,11 +134,11 @@ public: parseBase58(TokenType::NodePublic, jrrnUnl[x].asString()); BEAST_EXPECT(parsedKey); if (parsedKey) - BEAST_EXPECT(disabledKeys.find(*parsedKey) != disabledKeys.end()); + BEAST_EXPECT(disabledKeys.contains(*parsedKey)); } disabledKeys.clear(); - env.app().validators().setNegativeUNL(disabledKeys); + env.app().getValidators().setNegativeUNL(disabledKeys); auto const jrrUpdated = env.rpc("validators")[jss::result]; BEAST_EXPECT(jrrUpdated[jss::NegativeUNL].isNull()); } @@ -154,7 +154,7 @@ public: }; // Validator keys that will be in the published list - std::vector validators = { + std::vector const validators = { TrustedPublisherServer::randomValidator(), TrustedPublisherServer::randomValidator()}; std::set expectedKeys; for (auto const& val : validators) @@ -192,8 +192,8 @@ public: }), }; - env.app().validatorSites().start(); - env.app().validatorSites().join(); + env.app().getValidatorSites().start(); + env.app().getValidatorSites().join(); { auto const jrr = env.rpc("server_info")[jss::result]; @@ -252,8 +252,8 @@ public: }), }; - env.app().validatorSites().start(); - env.app().validatorSites().join(); + env.app().getValidatorSites().start(); + env.app().getValidatorSites().join(); { auto const jrr = env.rpc("server_info")[jss::result]; @@ -315,17 +315,17 @@ public: }), }; - env.app().validatorSites().start(); - env.app().validatorSites().join(); + env.app().getValidatorSites().start(); + env.app().getValidatorSites().join(); hash_set startKeys; for (auto const& val : validators) startKeys.insert(calcNodeID(val.masterPublic)); - env.app().validators().updateTrusted( + env.app().getValidators().updateTrusted( startKeys, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); { @@ -408,17 +408,17 @@ public: }), }; - env.app().validatorSites().start(); - env.app().validatorSites().join(); + env.app().getValidatorSites().start(); + env.app().getValidatorSites().join(); hash_set startKeys; for (auto const& val : validators) startKeys.insert(calcNodeID(val.masterPublic)); - env.app().validators().updateTrusted( + env.app().getValidators().updateTrusted( startKeys, env.timeKeeper().now(), env.app().getOPs(), - env.app().overlay(), + env.app().getOverlay(), env.app().getHashRouter()); { diff --git a/src/test/rpc/Version_test.cpp b/src/test/rpc/Version_test.cpp index c86bb8a660..c12e397459 100644 --- a/src/test/rpc/Version_test.cpp +++ b/src/test/rpc/Version_test.cpp @@ -42,10 +42,12 @@ class Version_test : public beast::unit_test::suite auto badVersion = [](Json::Value const& re) -> bool { if (re.isMember("error_what")) + { if (re["error_what"].isString()) { return re["error_what"].asString().find(jss::invalid_API_version.c_str()) == 0; } + } return false; }; @@ -75,13 +77,13 @@ class Version_test : public beast::unit_test::suite { testcase("test getAPIVersionNumber function"); - unsigned int versionIfUnspecified = + unsigned int const versionIfUnspecified = RPC::apiVersionIfUnspecified < RPC::apiMinimumSupportedVersion ? RPC::apiInvalidVersion : RPC::apiVersionIfUnspecified; - Json::Value j_array = Json::Value(Json::arrayValue); - Json::Value j_null = Json::Value(Json::nullValue); + Json::Value const j_array = Json::Value(Json::arrayValue); + Json::Value const j_null = Json::Value(Json::nullValue); BEAST_EXPECT(RPC::getAPIVersionNumber(j_array, false) == versionIfUnspecified); BEAST_EXPECT(RPC::getAPIVersionNumber(j_null, false) == versionIfUnspecified); @@ -183,7 +185,7 @@ class Version_test : public beast::unit_test::suite { testcase("config test"); { - Config c; + Config const c; BEAST_EXPECT(c.BETA_RPC_API == false); } diff --git a/src/test/server/ServerStatus_test.cpp b/src/test/server/ServerStatus_test.cpp index dc92375428..b7c9825a55 100644 --- a/src/test/server/ServerStatus_test.cpp +++ b/src/test/server/ServerStatus_test.cpp @@ -33,7 +33,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en { }; - auto + static auto makeConfig(std::string const& proto, bool admin = true, bool credentials = false) { auto const section_name = boost::starts_with(proto, "h") ? "port_rpc" : "port_ws"; @@ -69,7 +69,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en return p; } - auto + static auto makeWSUpgrade(std::string const& host, uint16_t port) { using namespace boost::asio; @@ -87,7 +87,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en std::random_device rd; std::mt19937 e{rd()}; std::uniform_int_distribution<> d(0, 255); - std::array key; + std::array key{}; for (auto& v : key) v = d(e); req.insert("Sec-WebSocket-Key", base64_encode(key.data(), key.size())); @@ -97,7 +97,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en return req; } - auto + static auto makeHTTPRequest( std::string const& host, uint16_t port, @@ -132,7 +132,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en void doRequest( boost::asio::yield_context& yield, - boost::beast::http::request&& req, + boost::beast::http::request const& req, std::string const& host, uint16_t port, bool secure, @@ -195,6 +195,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en { auto const port = env.app().config()["port_ws"].get("port"); auto ip = env.app().config()["port_ws"].get("ip"); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) doRequest(yield, makeWSUpgrade(*ip, *port), *ip, *port, secure, resp, ec); return; } @@ -211,11 +212,12 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en { auto const port = env.app().config()["port_rpc"].get("port"); auto const ip = env.app().config()["port_rpc"].get("ip"); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) doRequest(yield, makeHTTPRequest(*ip, *port, body, fields), *ip, *port, secure, resp, ec); return; } - auto + static auto makeAdminRequest( jtx::Env& env, std::string const& proto, @@ -286,6 +288,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en "admin_password"); // 1 - FAILS with wrong pass + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) jrr = makeAdminRequest(env, proto, *user, *password + "_")[jss::result]; BEAST_EXPECT(jrr["error"] == proto_ws ? "forbidden" : "noPermission"); BEAST_EXPECT( @@ -293,6 +296,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en : "You don't have permission for this command."); // 2 - FAILS with password in an object + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) jrr = makeAdminRequest(env, proto, *user, *password, true)[jss::result]; BEAST_EXPECT(jrr["error"] == proto_ws ? "forbidden" : "noPermission"); BEAST_EXPECT( @@ -300,6 +304,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en : "You don't have permission for this command."); // 3 - FAILS with wrong user + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) jrr = makeAdminRequest(env, proto, *user + "_", *password)[jss::result]; BEAST_EXPECT(jrr["error"] == proto_ws ? "forbidden" : "noPermission"); BEAST_EXPECT( @@ -314,6 +319,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en : "You don't have permission for this command."); // 5 - SUCCEEDS with proper credentials + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) jrr = makeAdminRequest(env, proto, *user, *password)[jss::result]; BEAST_EXPECT(jrr["status"] == "success"); } @@ -418,7 +424,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en boost::system::error_code ec; response resp; - auto req = makeWSUpgrade(*ip, *port); + auto req = makeWSUpgrade(*ip, *port); // NOLINT(bugprone-unchecked-optional-access) // truncate the request message to near the value of the version header auto req_string = boost::lexical_cast(req); @@ -428,7 +434,8 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en ip::tcp::resolver r{ios}; boost::beast::multi_buffer sb; - auto it = r.async_resolve(*ip, std::to_string(*port), yield[ec]); + auto it = r.async_resolve( + *ip, std::to_string(*port), yield[ec]); // NOLINT(bugprone-unchecked-optional-access) if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -509,8 +516,10 @@ 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(); // try with the correct user/pass, but not encoded @@ -538,7 +547,10 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en 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(); boost::system::error_code ec; @@ -560,7 +572,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en // for zero limit, pick an arbitrary nonzero number of clients - all // should connect fine. - int testTo = (limit == 0) ? 50 : limit + 1; + int const testTo = (limit == 0) ? 50 : limit + 1; while (connectionCount < testTo) { clients.emplace_back( @@ -596,15 +608,16 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en 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(); boost::beast::http::response resp; boost::system::error_code ec; doRequest(yield, makeWSUpgrade(ip, port), ip, port, true, resp, ec); BEAST_EXPECT(resp.result() == boost::beast::http::status::switching_protocols); - BEAST_EXPECT(resp.find("Upgrade") != resp.end() && resp["Upgrade"] == "websocket"); - BEAST_EXPECT( - resp.find("Connection") != resp.end() && boost::iequals(resp["Connection"], "upgrade")); + BEAST_EXPECT(resp.contains("Upgrade") && resp["Upgrade"] == "websocket"); + BEAST_EXPECT(resp.contains("Connection") && boost::iequals(resp["Connection"], "upgrade")); } void @@ -615,7 +628,9 @@ 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(); boost::beast::http::response resp; boost::system::error_code ec; @@ -636,7 +651,9 @@ 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(); boost::system::error_code ec; @@ -748,7 +765,16 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en response resp; doRequest( - yield, makeHTTPRequest(*ip_ws, *port_ws, "", {}), *ip_ws, *port_ws, false, resp, ec); + yield, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + makeHTTPRequest(*ip_ws, *port_ws, "", {}), + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *ip_ws, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *port_ws, + false, + resp, + ec); if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -784,7 +810,16 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en // but status does not indicate a problem doRequest( - yield, makeHTTPRequest(*ip_ws, *port_ws, "", {}), *ip_ws, *port_ws, false, resp, ec); + yield, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + makeHTTPRequest(*ip_ws, *port_ws, "", {}), + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *ip_ws, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *port_ws, + false, + resp, + ec); if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -795,7 +830,16 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en env.app().config().ELB_SUPPORT = true; doRequest( - yield, makeHTTPRequest(*ip_ws, *port_ws, "", {}), *ip_ws, *port_ws, false, resp, ec); + yield, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + makeHTTPRequest(*ip_ws, *port_ws, "", {}), + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *ip_ws, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *port_ws, + false, + resp, + ec); if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -849,7 +893,16 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en response resp; doRequest( - yield, makeHTTPRequest(*ip_ws, *port_ws, "", {}), *ip_ws, *port_ws, false, resp, ec); + yield, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + makeHTTPRequest(*ip_ws, *port_ws, "", {}), + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *ip_ws, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *port_ws, + false, + resp, + ec); if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -888,7 +941,16 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en // but status does not indicate because it still relies on ELB // being enabled doRequest( - yield, makeHTTPRequest(*ip_ws, *port_ws, "", {}), *ip_ws, *port_ws, false, resp, ec); + yield, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + makeHTTPRequest(*ip_ws, *port_ws, "", {}), + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *ip_ws, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *port_ws, + false, + resp, + ec); if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -898,7 +960,16 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en env.app().config().ELB_SUPPORT = true; doRequest( - yield, makeHTTPRequest(*ip_ws, *port_ws, "", {}), *ip_ws, *port_ws, false, resp, ec); + yield, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + makeHTTPRequest(*ip_ws, *port_ws, "", {}), + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *ip_ws, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *port_ws, + false, + resp, + ec); if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -1035,7 +1106,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en boost::system::error_code ec; doHTTPRequest(env, yield, false, resp, ec); BEAST_EXPECT(resp.result() == boost::beast::http::status::internal_server_error); - std::regex body{"Server cannot accept clients"}; + std::regex const body{"Server cannot accept clients"}; BEAST_EXPECT(std::regex_search(resp.body(), body)); } diff --git a/src/test/server/Server_test.cpp b/src/test/server/Server_test.cpp index f7250de7fa..97a822fd76 100644 --- a/src/test/server/Server_test.cpp +++ b/src/test/server/Server_test.cpp @@ -91,39 +91,43 @@ public: struct TestHandler { - bool + static bool onAccept(Session& session, boost::asio::ip::tcp::endpoint endpoint) { return true; } - Handoff + static Handoff onHandoff( Session& session, - std::unique_ptr&& bundle, - http_request_type&& request, + std::unique_ptr const& bundle, + http_request_type const& request, boost::asio::ip::tcp::endpoint remote_address) { return Handoff{}; } - Handoff + static Handoff onHandoff( Session& session, - http_request_type&& request, + http_request_type const& request, boost::asio::ip::tcp::endpoint remote_address) { return Handoff{}; } - void + static void onRequest(Session& session) { session.write(std::string("Hello, world!\n")); if (beast::rfc2616::is_keep_alive(session.request())) + { session.complete(); + } else + { session.close(true); + } } void @@ -232,7 +236,7 @@ public: return; boost::system::error_code ec; - s.shutdown(socket::shutdown_both, ec); + s.shutdown(socket::shutdown_both, ec); // NOLINT(bugprone-unused-return-value) std::this_thread::sleep_for(std::chrono::seconds(1)); } @@ -268,7 +272,7 @@ public: return; boost::system::error_code ec; - s.shutdown(socket::shutdown_both, ec); + s.shutdown(socket::shutdown_both, ec); // NOLINT(bugprone-unused-return-value) } void @@ -278,7 +282,7 @@ public: TestSink sink{*this}; TestThread thread; sink.threshold(beast::severities::Severity::kAll); - beast::Journal journal{sink}; + beast::Journal const journal{sink}; TestHandler handler; auto s = make_Server(handler, thread.get_io_context(), journal); std::vector serverPort(1); @@ -299,26 +303,26 @@ public: testcase("stress test"); struct NullHandler { - bool + static bool onAccept(Session& session, boost::asio::ip::tcp::endpoint endpoint) { return true; } - Handoff + static Handoff onHandoff( Session& session, - std::unique_ptr&& bundle, - http_request_type&& request, + std::unique_ptr const& bundle, + http_request_type const& request, boost::asio::ip::tcp::endpoint remote_address) { return Handoff{}; } - Handoff + static Handoff onHandoff( Session& session, - http_request_type&& request, + http_request_type const& request, boost::asio::ip::tcp::endpoint remote_address) { return Handoff{}; @@ -373,7 +377,7 @@ public: std::string messages; except([&] { - Env env{ + Env const env{ *this, envconfig([](std::unique_ptr cfg) { (*cfg).deprecatedClearSection("port_rpc"); @@ -384,7 +388,7 @@ public: BEAST_EXPECT(messages.find("Missing 'ip' in [port_rpc]") != std::string::npos); except([&] { - Env env{ + Env const env{ *this, envconfig([](std::unique_ptr cfg) { (*cfg).deprecatedClearSection("port_rpc"); @@ -396,7 +400,7 @@ public: BEAST_EXPECT(messages.find("Missing 'port' in [port_rpc]") != std::string::npos); except([&] { - Env env{ + Env const env{ *this, envconfig([](std::unique_ptr cfg) { (*cfg).deprecatedClearSection("port_rpc"); @@ -410,7 +414,7 @@ public: messages.find("Invalid value '0' for key 'port' in [port_rpc]") == std::string::npos); except([&] { - Env env{ + Env const env{ *this, envconfig([](std::unique_ptr cfg) { (*cfg)["server"].set("port", "0"); @@ -422,7 +426,7 @@ public: messages.find("Invalid value '0' for key 'port' in [server]") != std::string::npos); except([&] { - Env env{ + Env const env{ *this, envconfig([](std::unique_ptr cfg) { (*cfg).deprecatedClearSection("port_rpc"); @@ -438,7 +442,7 @@ public: except([&] // this creates a standard test config without the server // section { - Env env{ + Env const env{ *this, envconfig([](std::unique_ptr cfg) { cfg = std::make_unique(); @@ -467,7 +471,7 @@ public: except([&] // this creates a standard test config without some of the // port sections { - Env env{ + Env const env{ *this, envconfig([](std::unique_ptr cfg) { cfg = std::make_unique(); diff --git a/src/test/shamap/FetchPack_test.cpp b/src/test/shamap/FetchPack_test.cpp index 8919241996..1cf7d97b33 100644 --- a/src/test/shamap/FetchPack_test.cpp +++ b/src/test/shamap/FetchPack_test.cpp @@ -45,7 +45,7 @@ public: bool fromFilter, SHAMapHash const& nodeHash, std::uint32_t ledgerSeq, - Blob&& nodeData, + Blob&& nodeData, // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) SHAMapNodeType type) const override { } @@ -53,7 +53,7 @@ public: std::optional getNode(SHAMapHash const& nodeHash) const override { - Map::iterator it = mMap.find(nodeHash); + Map::iterator const it = mMap.find(nodeHash); if (it == mMap.end()) { JLOG(mJournal.fatal()) << "Test filter missing node"; @@ -66,7 +66,7 @@ public: beast::Journal mJournal; }; - boost::intrusive_ptr + static boost::intrusive_ptr make_random_item(beast::xor_shift_engine& r) { Serializer s; @@ -75,10 +75,10 @@ public: return make_shamapitem(s.getSHA512Half(), s.slice()); } - void + static void add_random_items(std::size_t n, Table& t, beast::xor_shift_engine& r) { - while (n--) + while ((n--) != 0u) { auto const result(t.addItem(SHAMapNodeType::tnACCOUNT_STATE, make_random_item(r))); assert(result); @@ -100,7 +100,7 @@ public: test::SuiteJournal journal("FetchPack_test", *this); TestNodeFamily f(journal); - std::shared_ptr t1(std::make_shared
(SHAMapType::FREE, f)); + std::shared_ptr
const t1(std::make_shared
(SHAMapType::FREE, f)); pass(); diff --git a/src/test/shamap/SHAMapSync_test.cpp b/src/test/shamap/SHAMapSync_test.cpp index 81305c4dab..6374e49e71 100644 --- a/src/test/shamap/SHAMapSync_test.cpp +++ b/src/test/shamap/SHAMapSync_test.cpp @@ -30,7 +30,7 @@ public: { // add a bunch of random states to a map, then remove them // map should be the same - SHAMapHash beforeHash = map.getHash(); + SHAMapHash const beforeHash = map.getHash(); std::list items; @@ -74,7 +74,7 @@ public: SHAMap source(SHAMapType::FREE, f); SHAMap destination(SHAMapType::FREE, f2); - int items = 10000; + int const items = 10000; for (int i = 0; i < items; ++i) { source.addItem(SHAMapNodeType::tnACCOUNT_STATE, makeRandomAS()); @@ -96,10 +96,6 @@ public: source.walkMap(missingNodes, 2048); BEAST_EXPECT(missingNodes.empty()); - std::vector nodeIDs, gotNodeIDs; - std::vector gotNodes; - std::vector hashes; - destination.setSynching(); { @@ -107,7 +103,7 @@ public: BEAST_EXPECT(source.getNodeFat(SHAMapNodeID(), a, rand_bool(eng_), rand_int(eng_, 2))); - unexpected(a.size() < 1, "NodeSize"); + unexpected(a.empty(), "NodeSize"); BEAST_EXPECT(destination.addRootNode(source.getHash(), makeSlice(a[0].second), nullptr) .isGood()); diff --git a/src/test/shamap/SHAMap_test.cpp b/src/test/shamap/SHAMap_test.cpp index 1eb050178c..1c6d62be97 100644 --- a/src/test/shamap/SHAMap_test.cpp +++ b/src/test/shamap/SHAMap_test.cpp @@ -110,9 +110,13 @@ public: run(bool backed, beast::Journal const& journal) { if (backed) + { testcase("add/traverse backed"); + } else + { testcase("add/traverse unbacked"); + } tests::TestNodeFamily f(journal); @@ -163,12 +167,16 @@ public: unexpected(i != e, "bad traverse"); if (backed) + { testcase("snapshot backed"); + } else + { testcase("snapshot unbacked"); + } - SHAMapHash mapHash = sMap.getHash(); - std::shared_ptr map2 = sMap.snapShot(false); + SHAMapHash const mapHash = sMap.getHash(); + std::shared_ptr const map2 = sMap.snapShot(false); map2->invariants(); unexpected(sMap.getHash() != mapHash, "bad snapshot"); unexpected(map2->getHash() != mapHash, "bad snapshot"); @@ -191,9 +199,13 @@ public: sMap.dump(); if (backed) + { testcase("build/tear backed"); + } else + { testcase("build/tear unbacked"); + } { constexpr std::array keys{ uint256( @@ -269,9 +281,13 @@ public: } if (backed) + { testcase("iterate backed"); + } else + { testcase("iterate unbacked"); + } { constexpr std::array keys{ @@ -354,7 +370,7 @@ class SHAMapPathProof_test : public beast::unit_test::suite path->insert(path->begin(), path->front()); BEAST_EXPECT(!map.verifyProofPath(root, k, *path)); // wrong key - uint256 wrongKey(c + 1); + uint256 const wrongKey(c + 1); BEAST_EXPECT(!map.getProofPath(wrongKey)); } if (c == 99) diff --git a/src/test/unit_test/SuiteJournal.h b/src/test/unit_test/SuiteJournal.h index 1eeefb68aa..93005401e6 100644 --- a/src/test/unit_test/SuiteJournal.h +++ b/src/test/unit_test/SuiteJournal.h @@ -70,7 +70,7 @@ SuiteJournalSink::writeAlways(beast::severities::Severity level, std::string con }(); static std::mutex log_mutex; - std::lock_guard lock(log_mutex); + std::lock_guard const lock(log_mutex); suite_.log << s << partition_ << text << std::endl; } @@ -87,11 +87,7 @@ public: : sink_(partition, threshold, suite), journal_(sink_) { } - // Clang 10.0.0 and 10.0.1 disagree about formatting operator& - // TBD Re-enable formatting when we upgrade to clang 11 - // clang-format off - operator beast::Journal &() - // clang-format on + operator beast::Journal&() { return journal_; } diff --git a/src/test/unit_test/multi_runner.cpp b/src/test/unit_test/multi_runner.cpp index 3838f128f0..e4fb1c4f45 100644 --- a/src/test/unit_test/multi_runner.cpp +++ b/src/test/unit_test/multi_runner.cpp @@ -108,7 +108,7 @@ results::print(S& s) { using namespace beast::unit_test; - if (top.size() > 0) + if (!top.empty()) { s << "Longest suite times:\n"; for (auto const& [name, dur] : top) @@ -154,7 +154,7 @@ template std::size_t multi_runner_base::inner::tests() const { - std::lock_guard l{m_}; + std::lock_guard const l{m_}; return results_.total; } @@ -162,7 +162,7 @@ template std::size_t multi_runner_base::inner::suites() const { - std::lock_guard l{m_}; + std::lock_guard const l{m_}; return results_.suites; } @@ -184,7 +184,7 @@ template void multi_runner_base::inner::add(results const& r) { - std::lock_guard l{m_}; + std::lock_guard const l{m_}; results_.merge(r); } @@ -193,7 +193,7 @@ template void multi_runner_base::inner::print_results(S& s) { - std::lock_guard l{m_}; + std::lock_guard const l{m_}; results_.print(s); } @@ -234,9 +234,13 @@ multi_runner_base::multi_runner_base() region_ = boost::interprocess::mapped_region{shared_mem_, boost::interprocess::read_write}; if (IsParent) + { inner_ = new (region_.get_address()) inner{}; + } else + { inner_ = reinterpret_cast(region_.get_address()); + } } catch (...) { @@ -322,7 +326,7 @@ void multi_runner_base::message_queue_send(MessageType mt, std::string const& s) { // must use a mutex since the two "sends" must happen in order - std::lock_guard l{inner_->m_}; + std::lock_guard const l{inner_->m_}; message_queue_->send(&mt, sizeof(mt), /*priority*/ 0); message_queue_->send(s.c_str(), s.size(), /*priority*/ 0); } @@ -382,7 +386,7 @@ multi_runner_parent::multi_runner_parent() : os_(std::cout) if (!recvd_size) continue; assert(recvd_size == 1); - MessageType mt{*reinterpret_cast(buf.data())}; + MessageType const mt{*reinterpret_cast(buf.data())}; this->message_queue_->receive(buf.data(), buf.size(), recvd_size, priority); if (recvd_size) diff --git a/src/test/unit_test/multi_runner.h b/src/test/unit_test/multi_runner.h index 8148079292..2eda4e66a0 100644 --- a/src/test/unit_test/multi_runner.h +++ b/src/test/unit_test/multi_runner.h @@ -131,10 +131,10 @@ class multi_runner_base print_results(S& s); }; - static constexpr char const* shared_mem_name_ = "RippledUnitTestSharedMem"; + static constexpr char const* shared_mem_name_ = "XrpldUnitTestSharedMem"; // name of the message queue a multi_runner_child will use to communicate // with multi_runner_parent - static constexpr char const* message_queue_name_ = "RippledUnitTestMessageQueue"; + static constexpr char const* message_queue_name_ = "XrpldUnitTestMessageQueue"; // `inner_` will be created in shared memory inner* inner_; diff --git a/src/test/unit_test/utils.h b/src/test/unit_test/utils.h new file mode 100644 index 0000000000..1f6ee58436 --- /dev/null +++ b/src/test/unit_test/utils.h @@ -0,0 +1,19 @@ +#include + +#include + +namespace xrpl { +namespace test { + +/// Compare two SecretKey objects for equality. +/// SecretKey::operator== is deleted, so a named function is used +/// to avoid member-function lookup shadowing free-function overloads. +inline bool +equal(SecretKey const& lhs, SecretKey const& rhs) +{ + return lhs.size() == SecretKey::size_ && rhs.size() == SecretKey::size_ && + std::memcmp(lhs.data(), rhs.data(), SecretKey::size_) == 0; +} + +} // namespace test +} // namespace xrpl diff --git a/src/tests/libxrpl/CMakeLists.txt b/src/tests/libxrpl/CMakeLists.txt index 6bcb6aff89..a82ed1472f 100644 --- a/src/tests/libxrpl/CMakeLists.txt +++ b/src/tests/libxrpl/CMakeLists.txt @@ -32,6 +32,21 @@ xrpl_add_test(json) target_link_libraries(xrpl.test.json PRIVATE xrpl.imports.test) add_dependencies(xrpl.tests xrpl.test.json) +# protocol_autogen tests use explicit source list (not GLOB) because sources are generated +# Mark generated sources so CMake knows they'll be created at build time +set_source_files_properties( + ${PROTOCOL_AUTOGEN_TEST_SOURCES} + PROPERTIES GENERATED TRUE +) +add_executable(xrpl.test.protocol_autogen ${PROTOCOL_AUTOGEN_TEST_SOURCES}) +target_link_libraries(xrpl.test.protocol_autogen PRIVATE xrpl.imports.test) +add_dependencies(xrpl.tests xrpl.test.protocol_autogen) +add_test(NAME xrpl.test.protocol_autogen COMMAND xrpl.test.protocol_autogen) +# Ensure code generation runs before compiling tests +if(TARGET protocol_autogen_generate) + add_dependencies(xrpl.test.protocol_autogen protocol_autogen_generate) +endif() + # Network unit tests are currently not supported on Windows if(NOT WIN32) xrpl_add_test(net) diff --git a/src/tests/libxrpl/basics/MallocTrim.cpp b/src/tests/libxrpl/basics/MallocTrim.cpp index f01bd91bbf..93ed48b885 100644 --- a/src/tests/libxrpl/basics/MallocTrim.cpp +++ b/src/tests/libxrpl/basics/MallocTrim.cpp @@ -52,8 +52,8 @@ TEST(parseStatmRSSkB, standard_format) // Test standard format: size resident shared text lib data dt // Assuming 4KB page size: resident=1000 pages = 4000 KB { - std::string statm = "25365 1000 2377 0 0 5623 0"; - long result = parseStatmRSSkB(statm); + std::string const statm = "25365 1000 2377 0 0 5623 0"; + long const result = parseStatmRSSkB(statm); // Note: actual result depends on system page size // On most systems it's 4KB, so 1000 pages = 4000 KB EXPECT_GT(result, 0); @@ -61,57 +61,57 @@ TEST(parseStatmRSSkB, standard_format) // Test with newline { - std::string statm = "12345 2000 1234 0 0 3456 0\n"; - long result = parseStatmRSSkB(statm); + std::string const statm = "12345 2000 1234 0 0 3456 0\n"; + long const result = parseStatmRSSkB(statm); EXPECT_GT(result, 0); } // Test with tabs { - std::string statm = "12345\t2000\t1234\t0\t0\t3456\t0"; - long result = parseStatmRSSkB(statm); + std::string const statm = "12345\t2000\t1234\t0\t0\t3456\t0"; + long const result = parseStatmRSSkB(statm); EXPECT_GT(result, 0); } // Test zero resident pages { - std::string statm = "25365 0 2377 0 0 5623 0"; - long result = parseStatmRSSkB(statm); + std::string const statm = "25365 0 2377 0 0 5623 0"; + long const result = parseStatmRSSkB(statm); EXPECT_EQ(result, 0); } // Test with extra whitespace { - std::string statm = " 25365 1000 2377 "; - long result = parseStatmRSSkB(statm); + std::string const statm = " 25365 1000 2377 "; + long const result = parseStatmRSSkB(statm); EXPECT_GT(result, 0); } // Test empty string { - std::string statm = ""; - long result = parseStatmRSSkB(statm); + std::string const statm; + long const result = parseStatmRSSkB(statm); EXPECT_EQ(result, -1); } // Test malformed data (only one field) { - std::string statm = "25365"; - long result = parseStatmRSSkB(statm); + std::string const statm = "25365"; + long const result = parseStatmRSSkB(statm); EXPECT_EQ(result, -1); } // Test malformed data (non-numeric) { - std::string statm = "abc def ghi"; - long result = parseStatmRSSkB(statm); + std::string const statm = "abc def ghi"; + long const result = parseStatmRSSkB(statm); EXPECT_EQ(result, -1); } // Test malformed data (second field non-numeric) { - std::string statm = "25365 abc 2377"; - long result = parseStatmRSSkB(statm); + std::string const statm = "25365 abc 2377"; + long const result = parseStatmRSSkB(statm); EXPECT_EQ(result, -1); } } @@ -119,9 +119,9 @@ TEST(parseStatmRSSkB, standard_format) TEST(mallocTrim, without_debug_logging) { - beast::Journal journal{beast::Journal::getNullSink()}; + beast::Journal const journal{beast::Journal::getNullSink()}; - MallocTrimReport report = mallocTrim("without_debug", journal); + MallocTrimReport const report = mallocTrim("without_debug", journal); #if defined(__GLIBC__) && BOOST_OS_LINUX EXPECT_EQ(report.supported, true); @@ -142,8 +142,8 @@ TEST(mallocTrim, without_debug_logging) TEST(mallocTrim, empty_tag) { - beast::Journal journal{beast::Journal::getNullSink()}; - MallocTrimReport report = mallocTrim("", journal); + beast::Journal const journal{beast::Journal::getNullSink()}; + MallocTrimReport const report = mallocTrim("", journal); #if defined(__GLIBC__) && BOOST_OS_LINUX EXPECT_EQ(report.supported, true); @@ -171,9 +171,9 @@ TEST(mallocTrim, with_debug_logging) }; DebugSink sink; - beast::Journal journal{sink}; + beast::Journal const journal{sink}; - MallocTrimReport report = mallocTrim("debug_test", journal); + MallocTrimReport const report = mallocTrim("debug_test", journal); #if defined(__GLIBC__) && BOOST_OS_LINUX EXPECT_EQ(report.supported, true); @@ -192,12 +192,12 @@ TEST(mallocTrim, with_debug_logging) TEST(mallocTrim, repeated_calls) { - beast::Journal journal{beast::Journal::getNullSink()}; + beast::Journal const journal{beast::Journal::getNullSink()}; // Call malloc_trim multiple times to ensure it's safe for (int i = 0; i < 5; ++i) { - MallocTrimReport report = mallocTrim("iteration_" + std::to_string(i), journal); + MallocTrimReport const report = mallocTrim("iteration_" + std::to_string(i), journal); #if defined(__GLIBC__) && BOOST_OS_LINUX EXPECT_EQ(report.supported, true); diff --git a/src/tests/libxrpl/basics/Mutex.cpp b/src/tests/libxrpl/basics/Mutex.cpp index 3bcee92276..9f58799fe7 100644 --- a/src/tests/libxrpl/basics/Mutex.cpp +++ b/src/tests/libxrpl/basics/Mutex.cpp @@ -183,7 +183,7 @@ TEST_F(MutexConstCorrectnessTest, non_const_allows_modification) TEST_F(MutexConstCorrectnessTest, const_reference_provides_const_access) { - Mutex> m({1, 2, 3, 4, 5, 6}); + Mutex> const m({1, 2, 3, 4, 5, 6}); Mutex> const& const_ref = m; auto lock = const_ref.lock(); static_assert(std::is_const_v>); @@ -225,7 +225,7 @@ struct MutexSharedMutexTest : ::testing::Test TEST_F(MutexSharedMutexTest, shared_lock_for_const_access) { - Mutex m(100); + Mutex const m(100); Mutex const& const_ref = m; auto lock = const_ref.lock(); EXPECT_EQ(*lock, 100); diff --git a/src/tests/libxrpl/basics/RangeSet.cpp b/src/tests/libxrpl/basics/RangeSet.cpp index d0fc656368..41a33133f2 100644 --- a/src/tests/libxrpl/basics/RangeSet.cpp +++ b/src/tests/libxrpl/basics/RangeSet.cpp @@ -17,7 +17,7 @@ TEST(RangeSet, prevMissing) RangeSet set; for (std::uint32_t i = 0; i < 10; ++i) - set.insert(range(10 * i, 10 * i + 5)); + set.insert(range(10 * i, (10 * i) + 5)); for (std::uint32_t i = 1; i < 100; ++i) { diff --git a/src/tests/libxrpl/basics/Slice.cpp b/src/tests/libxrpl/basics/Slice.cpp index 8fef0d61a3..72f2d081c7 100644 --- a/src/tests/libxrpl/basics/Slice.cpp +++ b/src/tests/libxrpl/basics/Slice.cpp @@ -28,24 +28,32 @@ TEST(Slice, equality_and_inequality) EXPECT_NE(s1.data(), nullptr); if (i == 0) + { EXPECT_EQ(s1, s0); + } else + { EXPECT_NE(s1, s0); + } for (std::size_t j = 0; j != sizeof(data); ++j) { Slice const s2{data, j}; if (i == j) + { EXPECT_EQ(s1, s2); + } else + { EXPECT_NE(s1, s2); + } } } // Test slices of equal size but pointing to different data: - std::array a; - std::array b; + std::array a{}; + std::array b{}; for (std::size_t i = 0; i != sizeof(data); ++i) a[i] = b[i] = data[i]; diff --git a/src/tests/libxrpl/basics/mulDiv.cpp b/src/tests/libxrpl/basics/mulDiv.cpp index c98c3fd61a..725bef399e 100644 --- a/src/tests/libxrpl/basics/mulDiv.cpp +++ b/src/tests/libxrpl/basics/mulDiv.cpp @@ -14,30 +14,30 @@ TEST(mulDiv, mulDiv) auto result = mulDiv(85, 20, 5); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 340); + EXPECT_EQ(*result, 340); // NOLINT(bugprone-unchecked-optional-access) result = mulDiv(20, 85, 5); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 340); + EXPECT_EQ(*result, 340); // NOLINT(bugprone-unchecked-optional-access) result = mulDiv(0, max - 1, max - 3); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 0); + EXPECT_EQ(*result, 0); // NOLINT(bugprone-unchecked-optional-access) result = mulDiv(max - 1, 0, max - 3); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 0); + EXPECT_EQ(*result, 0); // NOLINT(bugprone-unchecked-optional-access) result = mulDiv(max, 2, max / 2); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 4); + EXPECT_EQ(*result, 4); // NOLINT(bugprone-unchecked-optional-access) result = mulDiv(max, 1000, max / 1000); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 1000000); + EXPECT_EQ(*result, 1000000); // NOLINT(bugprone-unchecked-optional-access) result = mulDiv(max, 1000, max / 1001); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 1001000); + EXPECT_EQ(*result, 1001000); // NOLINT(bugprone-unchecked-optional-access) result = mulDiv(max32 + 1, max32 + 1, 5); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 3689348814741910323); + EXPECT_EQ(*result, 3689348814741910323); // NOLINT(bugprone-unchecked-optional-access) // Overflow result = mulDiv(max - 1, max - 2, 5); diff --git a/src/tests/libxrpl/basics/scope.cpp b/src/tests/libxrpl/basics/scope.cpp index 8efa4a84b1..067698bce4 100644 --- a/src/tests/libxrpl/basics/scope.cpp +++ b/src/tests/libxrpl/basics/scope.cpp @@ -10,7 +10,7 @@ TEST(scope, scope_exit) // unless release() is called int i = 0; { - scope_exit x{[&i]() { i = 1; }}; + scope_exit const x{[&i]() { i = 1; }}; } EXPECT_EQ(i, 1); { @@ -32,7 +32,7 @@ TEST(scope, scope_exit) { try { - scope_exit x{[&i]() { i = 5; }}; + scope_exit const x{[&i]() { i = 5; }}; throw 1; } catch (...) // NOLINT(bugprone-empty-catch) @@ -60,7 +60,7 @@ TEST(scope, scope_fail) // if an exception is unwinding, unless release() is called int i = 0; { - scope_fail x{[&i]() { i = 1; }}; + scope_fail const x{[&i]() { i = 1; }}; } EXPECT_EQ(i, 0); { @@ -82,7 +82,7 @@ TEST(scope, scope_fail) { try { - scope_fail x{[&i]() { i = 5; }}; + scope_fail const x{[&i]() { i = 5; }}; throw 1; } catch (...) // NOLINT(bugprone-empty-catch) @@ -110,7 +110,7 @@ TEST(scope, scope_success) // if an exception is not unwinding, unless release() is called int i = 0; { - scope_success x{[&i]() { i = 1; }}; + scope_success const x{[&i]() { i = 1; }}; } EXPECT_EQ(i, 1); { @@ -132,7 +132,7 @@ TEST(scope, scope_success) { try { - scope_success x{[&i]() { i = 5; }}; + scope_success const x{[&i]() { i = 5; }}; throw 1; } catch (...) // NOLINT(bugprone-empty-catch) diff --git a/src/tests/libxrpl/basics/tagged_integer.cpp b/src/tests/libxrpl/basics/tagged_integer.cpp index 09f8b6787b..85a246428b 100644 --- a/src/tests/libxrpl/basics/tagged_integer.cpp +++ b/src/tests/libxrpl/basics/tagged_integer.cpp @@ -147,7 +147,7 @@ TEST(tagged_integer, increment_decrement_operators) TEST(tagged_integer, arithmetic_operators) { - TagInt a{-2}; + TagInt const a{-2}; EXPECT_EQ(+a, TagInt{-2}); EXPECT_EQ(-a, TagInt{2}); EXPECT_EQ(TagInt{-3} + TagInt{4}, TagInt{1}); diff --git a/src/tests/libxrpl/json/Value.cpp b/src/tests/libxrpl/json/Value.cpp index cbca7c88a8..194c677024 100644 --- a/src/tests/libxrpl/json/Value.cpp +++ b/src/tests/libxrpl/json/Value.cpp @@ -38,7 +38,7 @@ TEST(json_value, construct_and_compare_Json_StaticString) EXPECT_EQ(test1, test2); EXPECT_NE(test1, test3); - std::string str{sample}; + std::string const str{sample}; EXPECT_EQ(str, test2); EXPECT_NE(str, test3); EXPECT_EQ(test2, str); @@ -52,7 +52,7 @@ TEST(json_value, different_types) auto testCopy = [](Json::ValueType typ) { Json::Value val{typ}; - Json::Value cpy{val}; + Json::Value const cpy{val}; EXPECT_EQ(val.type(), typ); EXPECT_EQ(cpy.type(), typ); return val; @@ -135,7 +135,7 @@ TEST(json_value, different_types) { Json::Value const staticStrV{staticStr}; { - Json::Value cpy{staticStrV}; + Json::Value const cpy{staticStrV}; EXPECT_EQ(staticStrV.type(), Json::stringValue); EXPECT_EQ(cpy.type(), Json::stringValue); } @@ -209,10 +209,10 @@ TEST(json_value, compare_strings) SCOPED_TRACE(line); EXPECT_EQ((lhs == rhs), lhsEqRhs); EXPECT_NE((lhs != rhs), lhsEqRhs); - EXPECT_EQ((lhs < rhs), (!(lhsEqRhs || !lhsLtRhs))); + EXPECT_EQ((lhs < rhs), (!lhsEqRhs && lhsLtRhs)); EXPECT_EQ((lhs <= rhs), (lhsEqRhs || lhsLtRhs)); EXPECT_EQ((lhs >= rhs), (lhsEqRhs || !lhsLtRhs)); - EXPECT_EQ((lhs > rhs), (!(lhsEqRhs || lhsLtRhs))); + EXPECT_EQ((lhs > rhs), (!lhsEqRhs && !lhsLtRhs)); }; Json::Value const null0; @@ -588,13 +588,13 @@ TEST(json_value, bad_json) TEST(json_value, edge_cases) { - std::uint32_t max_uint = std::numeric_limits::max(); - std::int32_t max_int = std::numeric_limits::max(); - std::int32_t min_int = std::numeric_limits::min(); + std::uint32_t const max_uint = std::numeric_limits::max(); + std::int32_t const max_int = std::numeric_limits::max(); + std::int32_t const min_int = std::numeric_limits::min(); - std::uint32_t a_uint = max_uint - 1978; - std::int32_t a_large_int = max_int - 1978; - std::int32_t a_small_int = min_int + 1978; + std::uint32_t const a_uint = max_uint - 1978; + std::int32_t const a_large_int = max_int - 1978; + std::int32_t const a_small_int = min_int + 1978; { std::string json = "{\"max_uint\":" + std::to_string(max_uint); @@ -628,7 +628,7 @@ TEST(json_value, edge_cases) EXPECT_LT(j1["a_small_int"], a_uint); } - std::uint64_t overflow = std::uint64_t(max_uint) + 1; + std::uint64_t const overflow = std::uint64_t(max_uint) + 1; { std::string json = "{\"overflow\":"; json += std::to_string(overflow); @@ -640,7 +640,7 @@ TEST(json_value, edge_cases) EXPECT_FALSE(r2.parse(json, j2)); } - std::int64_t underflow = std::int64_t(min_int) - 1; + std::int64_t const underflow = std::int64_t(min_int) - 1; { std::string json = "{\"underflow\":"; json += std::to_string(underflow); @@ -739,7 +739,7 @@ TEST(json_value, copy) EXPECT_TRUE(v1.isDouble()); EXPECT_EQ(v1.asDouble(), 2.5); - Json::Value v2 = v1; + Json::Value const v2 = v1; EXPECT_TRUE(v1.isDouble()); EXPECT_EQ(v1.asDouble(), 2.5); EXPECT_TRUE(v2.isDouble()); @@ -761,16 +761,16 @@ TEST(json_value, move) EXPECT_EQ(v1.asDouble(), 2.5); Json::Value v2 = std::move(v1); - EXPECT_FALSE(v1); + EXPECT_FALSE(v1); // NOLINT(bugprone-use-after-move) EXPECT_TRUE(v2.isDouble()); EXPECT_EQ(v2.asDouble(), 2.5); - EXPECT_NE(v1, v2); + EXPECT_NE(v1, v2); // NOLINT(bugprone-use-after-move) v1 = std::move(v2); EXPECT_TRUE(v1.isDouble()); EXPECT_EQ(v1.asDouble(), 2.5); - EXPECT_FALSE(v2); - EXPECT_NE(v1, v2); + EXPECT_FALSE(v2); // NOLINT(bugprone-use-after-move) + EXPECT_NE(v1, v2); // NOLINT(bugprone-use-after-move) } TEST(json_value, comparisons) @@ -819,7 +819,7 @@ TEST(json_value, comparisons) b["a"] = Json::Int(-1); testGreaterThan("negative"); - Json::Int big = std::numeric_limits::max(); + Json::Int const big = std::numeric_limits::max(); Json::UInt bigger = big; bigger++; @@ -859,7 +859,7 @@ TEST(json_value, conversions) // TODO: What's the thinking here? { // null - Json::Value val; + Json::Value const val; EXPECT_TRUE(val.isNull()); // val.asCString() should trigger an assertion failure EXPECT_EQ(val.asString(), ""); @@ -880,7 +880,7 @@ TEST(json_value, conversions) } { // int - Json::Value val = -1234; + Json::Value const val = -1234; EXPECT_TRUE(val.isInt()); // val.asCString() should trigger an assertion failure EXPECT_EQ(val.asString(), "-1234"); @@ -901,7 +901,7 @@ TEST(json_value, conversions) } { // uint - Json::Value val = 1234U; + Json::Value const val = 1234U; EXPECT_TRUE(val.isUInt()); // val.asCString() should trigger an assertion failure EXPECT_EQ(val.asString(), "1234"); @@ -922,7 +922,7 @@ TEST(json_value, conversions) } { // real - Json::Value val = 2.0; + Json::Value const val = 2.0; EXPECT_TRUE(val.isDouble()); // val.asCString() should trigger an assertion failure EXPECT_TRUE(std::regex_match(val.asString(), std::regex("^2\\.0*$"))); @@ -943,7 +943,7 @@ TEST(json_value, conversions) } { // numeric string - Json::Value val = "54321"; + Json::Value const val = "54321"; EXPECT_TRUE(val.isString()); EXPECT_EQ(strcmp(val.asCString(), "54321"), 0); EXPECT_EQ(val.asString(), "54321"); @@ -964,7 +964,7 @@ TEST(json_value, conversions) } { // non-numeric string - Json::Value val(Json::stringValue); + Json::Value const val(Json::stringValue); EXPECT_TRUE(val.isString()); EXPECT_EQ(val.asCString(), nullptr); EXPECT_EQ(val.asString(), ""); @@ -985,7 +985,7 @@ TEST(json_value, conversions) } { // bool false - Json::Value val = false; + Json::Value const val = false; EXPECT_TRUE(val.isBool()); // val.asCString() should trigger an assertion failure EXPECT_EQ(val.asString(), "false"); @@ -1006,7 +1006,7 @@ TEST(json_value, conversions) } { // bool true - Json::Value val = true; + Json::Value const val = true; EXPECT_TRUE(val.isBool()); // val.asCString() should trigger an assertion failure EXPECT_EQ(val.asString(), "true"); @@ -1027,7 +1027,7 @@ TEST(json_value, conversions) } { // array type - Json::Value val(Json::arrayValue); + Json::Value const val(Json::arrayValue); EXPECT_TRUE(val.isArray()); // val.asCString should trigger an assertion failure EXPECT_THROW(val.asString(), Json::error); @@ -1048,7 +1048,7 @@ TEST(json_value, conversions) } { // object type - Json::Value val(Json::objectValue); + Json::Value const val(Json::objectValue); EXPECT_TRUE(val.isObject()); // val.asCString should trigger an assertion failure EXPECT_THROW(val.asString(), Json::error); @@ -1348,7 +1348,7 @@ TEST(json_value, memory_leak) // Note that the type() == nullValue check is implementation // specific and not guaranteed to be valid in the future. - EXPECT_EQ(temp.type(), Json::nullValue); + EXPECT_EQ(temp.type(), Json::nullValue); // NOLINT(bugprone-use-after-move) } } diff --git a/src/tests/libxrpl/net/HTTPClient.cpp b/src/tests/libxrpl/net/HTTPClient.cpp index 1e93d47c3c..de567a93ab 100644 --- a/src/tests/libxrpl/net/HTTPClient.cpp +++ b/src/tests/libxrpl/net/HTTPClient.cpp @@ -33,7 +33,7 @@ private: boost::asio::ip::tcp::endpoint endpoint_; bool running_{true}; bool finished_{false}; - unsigned short port_; + unsigned short port_{0}; // Custom headers to return std::map customHeaders_; @@ -43,7 +43,7 @@ private: beast::Journal j_; public: - TestHTTPServer() : acceptor_(ioc_), port_(0), j_(TestSink::instance()) + TestHTTPServer() : acceptor_(ioc_), j_(TestSink::instance()) { // Bind to any available port endpoint_ = {boost::asio::ip::tcp::v4(), 0}; @@ -173,6 +173,8 @@ private: // Shutdown socket gracefully boost::system::error_code shutdownEc; + + // NOLINTNEXTLINE(bugprone-unused-return-value) socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, shutdownEc); } catch (std::exception const& e) @@ -183,64 +185,89 @@ private: } }; -// Helper function to run HTTP client test -bool -runHTTPTest( - TestHTTPServer& server, - std::string const& path, - bool& completed, - int& resultStatus, - std::string& resultData, - boost::system::error_code& resultError) -{ - // Create a null journal for testing - beast::Journal j{TestSink::instance()}; - - // Initialize HTTPClient SSL context - HTTPClient::initializeSSLContext("", "", false, j); - - HTTPClient::get( - false, // no SSL - server.ioc(), - "127.0.0.1", - server.port(), - path, - 1024, // max response size - std::chrono::seconds(5), - [&](boost::system::error_code const& ec, int status, std::string const& data) -> bool { - resultError = ec; - resultStatus = status; - resultData = data; - completed = true; - return false; // don't retry - }, - j); - - // Run the IO context until completion - auto start = std::chrono::steady_clock::now(); - while (server.ioc().run_one() != 0) - { - if (std::chrono::steady_clock::now() - start >= std::chrono::seconds(10) || - server.finished()) - { - break; - } - - if (completed) - { - server.stop(); - } - } - - return completed; -} - } // anonymous namespace -TEST(HTTPClient, case_insensitive_content_length) +// Test fixture that manages the SSL context lifecycle via RAII. +// SetUp() initializes the context before each test and TearDown() +// cleans it up afterwards, so individual tests don't need to worry +// about resource management. +class HTTPClientTest : public ::testing::Test +{ +protected: + // Shared journal for SSL context initialization and HTTP requests. + beast::Journal j_{TestSink::instance()}; + + // Initialize the global SSL context used by HTTPClient. + void + SetUp() override + { + HTTPClient::initializeSSLContext( + "" /* sslVerifyDir*/, "" /*sslVerifyFile */, false /* sslVerify */, j_ /* journal */); + } + + // Release the global SSL context to prevent memory leaks. + void + TearDown() override + { + HTTPClient::cleanupSSLContext(); + } + + // Issue an HTTP GET to the given test server and drive the + // io_context until a response arrives or a timeout is reached. + // Returns true when the completion handler was invoked. + bool + runHTTPTest( + TestHTTPServer& server, + std::string const& path, + bool& completed, + int& resultStatus, + std::string& resultData, + boost::system::error_code& resultError) + { + HTTPClient::get( + false, // no SSL + server.ioc(), + "127.0.0.1", + server.port(), + path, + 1024, // max response size + std::chrono::seconds(5), + [&](boost::system::error_code const& ec, int status, std::string const& data) -> bool { + resultError = ec; + resultStatus = status; + resultData = data; + completed = true; + return false; // don't retry + }, + j_); + + // Run the IO context until completion + auto start = std::chrono::steady_clock::now(); + while (server.ioc().run_one() != 0) + { + if (std::chrono::steady_clock::now() - start >= std::chrono::seconds(10) || + server.finished()) + { + break; + } + + if (completed) + { + server.stop(); + } + } + + // Drain any remaining handlers to ensure proper cleanup of HTTPClientImp + server.ioc().poll(); + + return completed; + } +}; + +TEST_F(HTTPClientTest, case_insensitive_content_length) { // Test different cases of Content-Length header - std::vector headerCases = { + std::vector const headerCases = { "Content-Length", // Standard case "content-length", // Lowercase - this tests the regex icase fix "CONTENT-LENGTH", // Uppercase @@ -251,7 +278,7 @@ TEST(HTTPClient, case_insensitive_content_length) for (auto const& headerName : headerCases) { TestHTTPServer server; - std::string testBody = "Hello World!"; + std::string const testBody = "Hello World!"; server.setResponseBody(testBody); server.setHeader(headerName, std::to_string(testBody.size())); @@ -260,9 +287,8 @@ TEST(HTTPClient, case_insensitive_content_length) std::string resultData; boost::system::error_code resultError; - bool testCompleted = + bool const testCompleted = runHTTPTest(server, "/test", completed, resultStatus, resultData, resultError); - // Verify results EXPECT_TRUE(testCompleted); EXPECT_FALSE(resultError); @@ -271,10 +297,10 @@ TEST(HTTPClient, case_insensitive_content_length) } } -TEST(HTTPClient, basic_http_request) +TEST_F(HTTPClientTest, basic_http_request) { TestHTTPServer server; - std::string testBody = "Test response body"; + std::string const testBody = "Test response body"; server.setResponseBody(testBody); server.setHeader("Content-Type", "text/plain"); @@ -283,7 +309,7 @@ TEST(HTTPClient, basic_http_request) std::string resultData; boost::system::error_code resultError; - bool testCompleted = + bool const testCompleted = runHTTPTest(server, "/basic", completed, resultStatus, resultData, resultError); EXPECT_TRUE(testCompleted); @@ -292,7 +318,7 @@ TEST(HTTPClient, basic_http_request) EXPECT_EQ(resultData, testBody); } -TEST(HTTPClient, empty_response) +TEST_F(HTTPClientTest, empty_response) { TestHTTPServer server; server.setResponseBody(""); // Empty body @@ -303,7 +329,7 @@ TEST(HTTPClient, empty_response) std::string resultData; boost::system::error_code resultError; - bool testCompleted = + bool const testCompleted = runHTTPTest(server, "/empty", completed, resultStatus, resultData, resultError); EXPECT_TRUE(testCompleted); @@ -312,9 +338,9 @@ TEST(HTTPClient, empty_response) EXPECT_TRUE(resultData.empty()); } -TEST(HTTPClient, different_status_codes) +TEST_F(HTTPClientTest, different_status_codes) { - std::vector statusCodes = {200, 404, 500}; + std::vector const statusCodes = {200, 404, 500}; for (auto status : statusCodes) { @@ -327,7 +353,7 @@ TEST(HTTPClient, different_status_codes) std::string resultData; boost::system::error_code resultError; - bool testCompleted = + bool const testCompleted = runHTTPTest(server, "/status", completed, resultStatus, resultData, resultError); EXPECT_TRUE(testCompleted); diff --git a/src/tests/libxrpl/protocol_autogen/.clang-tidy b/src/tests/libxrpl/protocol_autogen/.clang-tidy new file mode 100644 index 0000000000..fbc003598d --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/.clang-tidy @@ -0,0 +1,3 @@ +# This disables all checks for this directory and its subdirectories +Checks: "-*" +InheritParentConfig: false diff --git a/src/tests/libxrpl/protocol_autogen/TestHelpers.h b/src/tests/libxrpl/protocol_autogen/TestHelpers.h new file mode 100644 index 0000000000..cf2d7ffe6a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/TestHelpers.h @@ -0,0 +1,209 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace xrpl { + +// Typed field canonical values + +using Uint8Value = std::decay_t; +inline Uint8Value +canonical_UINT8() +{ + return Uint8Value{1}; +} + +using Uint16Value = std::decay_t; +inline Uint16Value +canonical_UINT16() +{ + return Uint16Value{1}; +} + +using Uint32Value = std::decay_t; +inline Uint32Value +canonical_UINT32() +{ + return Uint32Value{1}; +} + +using Uint64Value = std::decay_t; +inline Uint64Value +canonical_UINT64() +{ + return Uint64Value{1}; +} + +using Uint128Value = std::decay_t; +inline Uint128Value +canonical_UINT128() +{ + return Uint128Value{1}; +} + +using Uint160Value = std::decay_t; +inline Uint160Value +canonical_UINT160() +{ + return Uint160Value{1}; +} + +using Uint192Value = std::decay_t; +inline Uint192Value +canonical_UINT192() +{ + return Uint192Value{1}; +} + +using Uint256Value = std::decay_t; +inline Uint256Value +canonical_UINT256() +{ + return Uint256Value{1}; +} + +using Int32Value = std::decay_t; +inline Int32Value +canonical_INT32() +{ + return Int32Value{42}; +} + +using NumberValue = std::decay_t; +inline NumberValue +canonical_NUMBER() +{ + return NumberValue{123}; +} + +using AmountValue = std::decay_t; +inline AmountValue +canonical_AMOUNT() +{ + return AmountValue{XRPAmount{1}}; +} + +using AccountValue = std::decay_t; +inline AccountValue +canonical_ACCOUNT() +{ + return xrpAccount(); +} + +using CurrencyValue = std::decay_t; +inline CurrencyValue +canonical_CURRENCY() +{ + return xrpCurrency(); +} + +using IssueValue = std::decay_t; +inline IssueValue +canonical_ISSUE() +{ + return IssueValue{xrpIssue()}; +} + +using Vector256Value = std::decay_t; +inline Vector256Value +canonical_VECTOR256() +{ + return Vector256Value{uint256{1}}; +} + +using BlobValue = std::decay_t; +inline BlobValue +canonical_VL() +{ + static constexpr std::array data{{'a', 'b', 'c'}}; + return BlobValue{data.data(), data.size()}; +} + +using XChainBridgeValue = std::decay_t; +inline XChainBridgeValue +canonical_XCHAIN_BRIDGE() +{ + return XChainBridgeValue{xrpAccount(), xrpIssue(), xrpAccount(), xrpIssue()}; +} + +// Untyped field canonical values + +inline STArray +canonical_ARRAY() +{ + return STArray{}; +} + +inline STObject +canonical_OBJECT() +{ + return STObject{sfGeneric}; +} + +inline STPathSet +canonical_PATHSET() +{ + STPathSet result{}; + result.push_back(STPath{}); + return result; +} + +// Field comparison helpers for generated tests + +template +void +expectEqualField(T const& expected, T const& actual, char const* fieldName) +{ + EXPECT_EQ(expected, actual) << "Field " << fieldName << " mismatch"; +} + +// Specialization for STObject (no operator==, use isEquivalent) +template <> +inline void +expectEqualField(STObject const& expected, STObject const& actual, char const* fieldName) +{ + EXPECT_TRUE(expected.isEquivalent(actual)) << "Field " << fieldName << " mismatch"; +} + +// Specialization for STPathSet (no operator==, use isEquivalent) +template <> +inline void +expectEqualField( + STPathSet const& expected, + STPathSet const& actual, + char const* fieldName) +{ + EXPECT_TRUE(expected.isEquivalent(actual)) << "Field " << fieldName << " mismatch"; +} + +// Overloads for std::reference_wrapper (used by optional ARRAY/PATHSET fields) +template +void +expectEqualField(T const& expected, std::reference_wrapper actual, char const* fieldName) +{ + expectEqualField(expected, actual.get(), fieldName); +} + +} // namespace xrpl diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/AMMTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/AMMTests.cpp new file mode 100644 index 0000000000..0bb80c41e6 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/AMMTests.cpp @@ -0,0 +1,361 @@ +// Auto-generated unit tests for ledger entry AMM + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(AMMTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const tradingFeeValue = canonical_UINT16(); + auto const voteSlotsValue = canonical_ARRAY(); + auto const auctionSlotValue = canonical_OBJECT(); + auto const lPTokenBalanceValue = canonical_AMOUNT(); + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + AMMBuilder builder{ + accountValue, + lPTokenBalanceValue, + assetValue, + asset2Value, + ownerNodeValue + }; + + builder.setTradingFee(tradingFeeValue); + builder.setVoteSlots(voteSlotsValue); + builder.setAuctionSlot(auctionSlotValue); + builder.setPreviousTxnID(previousTxnIDValue); + builder.setPreviousTxnLgrSeq(previousTxnLgrSeqValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = lPTokenBalanceValue; + auto const actual = entry.getLPTokenBalance(); + expectEqualField(expected, actual, "sfLPTokenBalance"); + } + + { + auto const& expected = assetValue; + auto const actual = entry.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = entry.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = tradingFeeValue; + auto const actualOpt = entry.getTradingFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTradingFee"); + EXPECT_TRUE(entry.hasTradingFee()); + } + + { + auto const& expected = voteSlotsValue; + auto const actualOpt = entry.getVoteSlots(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfVoteSlots"); + EXPECT_TRUE(entry.hasVoteSlots()); + } + + { + auto const& expected = auctionSlotValue; + auto const actualOpt = entry.getAuctionSlot(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAuctionSlot"); + EXPECT_TRUE(entry.hasAuctionSlot()); + } + + { + auto const& expected = previousTxnIDValue; + auto const actualOpt = entry.getPreviousTxnID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnID"); + EXPECT_TRUE(entry.hasPreviousTxnID()); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actualOpt = entry.getPreviousTxnLgrSeq(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnLgrSeq"); + EXPECT_TRUE(entry.hasPreviousTxnLgrSeq()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(AMMTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const tradingFeeValue = canonical_UINT16(); + auto const voteSlotsValue = canonical_ARRAY(); + auto const auctionSlotValue = canonical_OBJECT(); + auto const lPTokenBalanceValue = canonical_AMOUNT(); + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(AMM::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfTradingFee) = tradingFeeValue; + sle->setFieldArray(sfVoteSlots, voteSlotsValue); + sle->setFieldObject(sfAuctionSlot, auctionSlotValue); + sle->at(sfLPTokenBalance) = lPTokenBalanceValue; + sle->at(sfAsset) = STIssue(sfAsset, assetValue); + sle->at(sfAsset2) = STIssue(sfAsset2, asset2Value); + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + AMMBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + AMM entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = lPTokenBalanceValue; + + auto const fromSle = entryFromSle.getLPTokenBalance(); + auto const fromBuilder = entryFromBuilder.getLPTokenBalance(); + + expectEqualField(expected, fromSle, "sfLPTokenBalance"); + expectEqualField(expected, fromBuilder, "sfLPTokenBalance"); + } + + { + auto const& expected = assetValue; + + auto const fromSle = entryFromSle.getAsset(); + auto const fromBuilder = entryFromBuilder.getAsset(); + + expectEqualField(expected, fromSle, "sfAsset"); + expectEqualField(expected, fromBuilder, "sfAsset"); + } + + { + auto const& expected = asset2Value; + + auto const fromSle = entryFromSle.getAsset2(); + auto const fromBuilder = entryFromBuilder.getAsset2(); + + expectEqualField(expected, fromSle, "sfAsset2"); + expectEqualField(expected, fromBuilder, "sfAsset2"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = tradingFeeValue; + + auto const fromSleOpt = entryFromSle.getTradingFee(); + auto const fromBuilderOpt = entryFromBuilder.getTradingFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTradingFee"); + expectEqualField(expected, *fromBuilderOpt, "sfTradingFee"); + } + + { + auto const& expected = voteSlotsValue; + + auto const fromSleOpt = entryFromSle.getVoteSlots(); + auto const fromBuilderOpt = entryFromBuilder.getVoteSlots(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfVoteSlots"); + expectEqualField(expected, *fromBuilderOpt, "sfVoteSlots"); + } + + { + auto const& expected = auctionSlotValue; + + auto const fromSleOpt = entryFromSle.getAuctionSlot(); + auto const fromBuilderOpt = entryFromBuilder.getAuctionSlot(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAuctionSlot"); + expectEqualField(expected, *fromBuilderOpt, "sfAuctionSlot"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnID(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnID"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnLgrSeq(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(AMMTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(AMM{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(AMMTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(AMMBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(AMMTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const lPTokenBalanceValue = canonical_AMOUNT(); + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const ownerNodeValue = canonical_UINT64(); + + AMMBuilder builder{ + accountValue, + lPTokenBalanceValue, + assetValue, + asset2Value, + ownerNodeValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasTradingFee()); + EXPECT_FALSE(entry.getTradingFee().has_value()); + EXPECT_FALSE(entry.hasVoteSlots()); + EXPECT_FALSE(entry.getVoteSlots().has_value()); + EXPECT_FALSE(entry.hasAuctionSlot()); + EXPECT_FALSE(entry.getAuctionSlot().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnID()); + EXPECT_FALSE(entry.getPreviousTxnID().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnLgrSeq()); + EXPECT_FALSE(entry.getPreviousTxnLgrSeq().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/AccountRootTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/AccountRootTests.cpp new file mode 100644 index 0000000000..e967b614b7 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/AccountRootTests.cpp @@ -0,0 +1,707 @@ +// Auto-generated unit tests for ledger entry AccountRoot + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(AccountRootTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const balanceValue = canonical_AMOUNT(); + auto const ownerCountValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const accountTxnIDValue = canonical_UINT256(); + auto const regularKeyValue = canonical_ACCOUNT(); + auto const emailHashValue = canonical_UINT128(); + auto const walletLocatorValue = canonical_UINT256(); + auto const walletSizeValue = canonical_UINT32(); + auto const messageKeyValue = canonical_VL(); + auto const transferRateValue = canonical_UINT32(); + auto const domainValue = canonical_VL(); + auto const tickSizeValue = canonical_UINT8(); + auto const ticketCountValue = canonical_UINT32(); + auto const nFTokenMinterValue = canonical_ACCOUNT(); + auto const mintedNFTokensValue = canonical_UINT32(); + auto const burnedNFTokensValue = canonical_UINT32(); + auto const firstNFTokenSequenceValue = canonical_UINT32(); + auto const aMMIDValue = canonical_UINT256(); + auto const vaultIDValue = canonical_UINT256(); + auto const loanBrokerIDValue = canonical_UINT256(); + + AccountRootBuilder builder{ + accountValue, + sequenceValue, + balanceValue, + ownerCountValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setAccountTxnID(accountTxnIDValue); + builder.setRegularKey(regularKeyValue); + builder.setEmailHash(emailHashValue); + builder.setWalletLocator(walletLocatorValue); + builder.setWalletSize(walletSizeValue); + builder.setMessageKey(messageKeyValue); + builder.setTransferRate(transferRateValue); + builder.setDomain(domainValue); + builder.setTickSize(tickSizeValue); + builder.setTicketCount(ticketCountValue); + builder.setNFTokenMinter(nFTokenMinterValue); + builder.setMintedNFTokens(mintedNFTokensValue); + builder.setBurnedNFTokens(burnedNFTokensValue); + builder.setFirstNFTokenSequence(firstNFTokenSequenceValue); + builder.setAMMID(aMMIDValue); + builder.setVaultID(vaultIDValue); + builder.setLoanBrokerID(loanBrokerIDValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = sequenceValue; + auto const actual = entry.getSequence(); + expectEqualField(expected, actual, "sfSequence"); + } + + { + auto const& expected = balanceValue; + auto const actual = entry.getBalance(); + expectEqualField(expected, actual, "sfBalance"); + } + + { + auto const& expected = ownerCountValue; + auto const actual = entry.getOwnerCount(); + expectEqualField(expected, actual, "sfOwnerCount"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = accountTxnIDValue; + auto const actualOpt = entry.getAccountTxnID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAccountTxnID"); + EXPECT_TRUE(entry.hasAccountTxnID()); + } + + { + auto const& expected = regularKeyValue; + auto const actualOpt = entry.getRegularKey(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfRegularKey"); + EXPECT_TRUE(entry.hasRegularKey()); + } + + { + auto const& expected = emailHashValue; + auto const actualOpt = entry.getEmailHash(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfEmailHash"); + EXPECT_TRUE(entry.hasEmailHash()); + } + + { + auto const& expected = walletLocatorValue; + auto const actualOpt = entry.getWalletLocator(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfWalletLocator"); + EXPECT_TRUE(entry.hasWalletLocator()); + } + + { + auto const& expected = walletSizeValue; + auto const actualOpt = entry.getWalletSize(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfWalletSize"); + EXPECT_TRUE(entry.hasWalletSize()); + } + + { + auto const& expected = messageKeyValue; + auto const actualOpt = entry.getMessageKey(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMessageKey"); + EXPECT_TRUE(entry.hasMessageKey()); + } + + { + auto const& expected = transferRateValue; + auto const actualOpt = entry.getTransferRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTransferRate"); + EXPECT_TRUE(entry.hasTransferRate()); + } + + { + auto const& expected = domainValue; + auto const actualOpt = entry.getDomain(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDomain"); + EXPECT_TRUE(entry.hasDomain()); + } + + { + auto const& expected = tickSizeValue; + auto const actualOpt = entry.getTickSize(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTickSize"); + EXPECT_TRUE(entry.hasTickSize()); + } + + { + auto const& expected = ticketCountValue; + auto const actualOpt = entry.getTicketCount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTicketCount"); + EXPECT_TRUE(entry.hasTicketCount()); + } + + { + auto const& expected = nFTokenMinterValue; + auto const actualOpt = entry.getNFTokenMinter(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfNFTokenMinter"); + EXPECT_TRUE(entry.hasNFTokenMinter()); + } + + { + auto const& expected = mintedNFTokensValue; + auto const actualOpt = entry.getMintedNFTokens(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMintedNFTokens"); + EXPECT_TRUE(entry.hasMintedNFTokens()); + } + + { + auto const& expected = burnedNFTokensValue; + auto const actualOpt = entry.getBurnedNFTokens(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfBurnedNFTokens"); + EXPECT_TRUE(entry.hasBurnedNFTokens()); + } + + { + auto const& expected = firstNFTokenSequenceValue; + auto const actualOpt = entry.getFirstNFTokenSequence(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfFirstNFTokenSequence"); + EXPECT_TRUE(entry.hasFirstNFTokenSequence()); + } + + { + auto const& expected = aMMIDValue; + auto const actualOpt = entry.getAMMID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAMMID"); + EXPECT_TRUE(entry.hasAMMID()); + } + + { + auto const& expected = vaultIDValue; + auto const actualOpt = entry.getVaultID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfVaultID"); + EXPECT_TRUE(entry.hasVaultID()); + } + + { + auto const& expected = loanBrokerIDValue; + auto const actualOpt = entry.getLoanBrokerID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLoanBrokerID"); + EXPECT_TRUE(entry.hasLoanBrokerID()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(AccountRootTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const balanceValue = canonical_AMOUNT(); + auto const ownerCountValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const accountTxnIDValue = canonical_UINT256(); + auto const regularKeyValue = canonical_ACCOUNT(); + auto const emailHashValue = canonical_UINT128(); + auto const walletLocatorValue = canonical_UINT256(); + auto const walletSizeValue = canonical_UINT32(); + auto const messageKeyValue = canonical_VL(); + auto const transferRateValue = canonical_UINT32(); + auto const domainValue = canonical_VL(); + auto const tickSizeValue = canonical_UINT8(); + auto const ticketCountValue = canonical_UINT32(); + auto const nFTokenMinterValue = canonical_ACCOUNT(); + auto const mintedNFTokensValue = canonical_UINT32(); + auto const burnedNFTokensValue = canonical_UINT32(); + auto const firstNFTokenSequenceValue = canonical_UINT32(); + auto const aMMIDValue = canonical_UINT256(); + auto const vaultIDValue = canonical_UINT256(); + auto const loanBrokerIDValue = canonical_UINT256(); + + auto sle = std::make_shared(AccountRoot::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfBalance) = balanceValue; + sle->at(sfOwnerCount) = ownerCountValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfAccountTxnID) = accountTxnIDValue; + sle->at(sfRegularKey) = regularKeyValue; + sle->at(sfEmailHash) = emailHashValue; + sle->at(sfWalletLocator) = walletLocatorValue; + sle->at(sfWalletSize) = walletSizeValue; + sle->at(sfMessageKey) = messageKeyValue; + sle->at(sfTransferRate) = transferRateValue; + sle->at(sfDomain) = domainValue; + sle->at(sfTickSize) = tickSizeValue; + sle->at(sfTicketCount) = ticketCountValue; + sle->at(sfNFTokenMinter) = nFTokenMinterValue; + sle->at(sfMintedNFTokens) = mintedNFTokensValue; + sle->at(sfBurnedNFTokens) = burnedNFTokensValue; + sle->at(sfFirstNFTokenSequence) = firstNFTokenSequenceValue; + sle->at(sfAMMID) = aMMIDValue; + sle->at(sfVaultID) = vaultIDValue; + sle->at(sfLoanBrokerID) = loanBrokerIDValue; + + AccountRootBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + AccountRoot entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSle = entryFromSle.getSequence(); + auto const fromBuilder = entryFromBuilder.getSequence(); + + expectEqualField(expected, fromSle, "sfSequence"); + expectEqualField(expected, fromBuilder, "sfSequence"); + } + + { + auto const& expected = balanceValue; + + auto const fromSle = entryFromSle.getBalance(); + auto const fromBuilder = entryFromBuilder.getBalance(); + + expectEqualField(expected, fromSle, "sfBalance"); + expectEqualField(expected, fromBuilder, "sfBalance"); + } + + { + auto const& expected = ownerCountValue; + + auto const fromSle = entryFromSle.getOwnerCount(); + auto const fromBuilder = entryFromBuilder.getOwnerCount(); + + expectEqualField(expected, fromSle, "sfOwnerCount"); + expectEqualField(expected, fromBuilder, "sfOwnerCount"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = accountTxnIDValue; + + auto const fromSleOpt = entryFromSle.getAccountTxnID(); + auto const fromBuilderOpt = entryFromBuilder.getAccountTxnID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAccountTxnID"); + expectEqualField(expected, *fromBuilderOpt, "sfAccountTxnID"); + } + + { + auto const& expected = regularKeyValue; + + auto const fromSleOpt = entryFromSle.getRegularKey(); + auto const fromBuilderOpt = entryFromBuilder.getRegularKey(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfRegularKey"); + expectEqualField(expected, *fromBuilderOpt, "sfRegularKey"); + } + + { + auto const& expected = emailHashValue; + + auto const fromSleOpt = entryFromSle.getEmailHash(); + auto const fromBuilderOpt = entryFromBuilder.getEmailHash(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfEmailHash"); + expectEqualField(expected, *fromBuilderOpt, "sfEmailHash"); + } + + { + auto const& expected = walletLocatorValue; + + auto const fromSleOpt = entryFromSle.getWalletLocator(); + auto const fromBuilderOpt = entryFromBuilder.getWalletLocator(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfWalletLocator"); + expectEqualField(expected, *fromBuilderOpt, "sfWalletLocator"); + } + + { + auto const& expected = walletSizeValue; + + auto const fromSleOpt = entryFromSle.getWalletSize(); + auto const fromBuilderOpt = entryFromBuilder.getWalletSize(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfWalletSize"); + expectEqualField(expected, *fromBuilderOpt, "sfWalletSize"); + } + + { + auto const& expected = messageKeyValue; + + auto const fromSleOpt = entryFromSle.getMessageKey(); + auto const fromBuilderOpt = entryFromBuilder.getMessageKey(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMessageKey"); + expectEqualField(expected, *fromBuilderOpt, "sfMessageKey"); + } + + { + auto const& expected = transferRateValue; + + auto const fromSleOpt = entryFromSle.getTransferRate(); + auto const fromBuilderOpt = entryFromBuilder.getTransferRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTransferRate"); + expectEqualField(expected, *fromBuilderOpt, "sfTransferRate"); + } + + { + auto const& expected = domainValue; + + auto const fromSleOpt = entryFromSle.getDomain(); + auto const fromBuilderOpt = entryFromBuilder.getDomain(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDomain"); + expectEqualField(expected, *fromBuilderOpt, "sfDomain"); + } + + { + auto const& expected = tickSizeValue; + + auto const fromSleOpt = entryFromSle.getTickSize(); + auto const fromBuilderOpt = entryFromBuilder.getTickSize(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTickSize"); + expectEqualField(expected, *fromBuilderOpt, "sfTickSize"); + } + + { + auto const& expected = ticketCountValue; + + auto const fromSleOpt = entryFromSle.getTicketCount(); + auto const fromBuilderOpt = entryFromBuilder.getTicketCount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTicketCount"); + expectEqualField(expected, *fromBuilderOpt, "sfTicketCount"); + } + + { + auto const& expected = nFTokenMinterValue; + + auto const fromSleOpt = entryFromSle.getNFTokenMinter(); + auto const fromBuilderOpt = entryFromBuilder.getNFTokenMinter(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfNFTokenMinter"); + expectEqualField(expected, *fromBuilderOpt, "sfNFTokenMinter"); + } + + { + auto const& expected = mintedNFTokensValue; + + auto const fromSleOpt = entryFromSle.getMintedNFTokens(); + auto const fromBuilderOpt = entryFromBuilder.getMintedNFTokens(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMintedNFTokens"); + expectEqualField(expected, *fromBuilderOpt, "sfMintedNFTokens"); + } + + { + auto const& expected = burnedNFTokensValue; + + auto const fromSleOpt = entryFromSle.getBurnedNFTokens(); + auto const fromBuilderOpt = entryFromBuilder.getBurnedNFTokens(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfBurnedNFTokens"); + expectEqualField(expected, *fromBuilderOpt, "sfBurnedNFTokens"); + } + + { + auto const& expected = firstNFTokenSequenceValue; + + auto const fromSleOpt = entryFromSle.getFirstNFTokenSequence(); + auto const fromBuilderOpt = entryFromBuilder.getFirstNFTokenSequence(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfFirstNFTokenSequence"); + expectEqualField(expected, *fromBuilderOpt, "sfFirstNFTokenSequence"); + } + + { + auto const& expected = aMMIDValue; + + auto const fromSleOpt = entryFromSle.getAMMID(); + auto const fromBuilderOpt = entryFromBuilder.getAMMID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAMMID"); + expectEqualField(expected, *fromBuilderOpt, "sfAMMID"); + } + + { + auto const& expected = vaultIDValue; + + auto const fromSleOpt = entryFromSle.getVaultID(); + auto const fromBuilderOpt = entryFromBuilder.getVaultID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfVaultID"); + expectEqualField(expected, *fromBuilderOpt, "sfVaultID"); + } + + { + auto const& expected = loanBrokerIDValue; + + auto const fromSleOpt = entryFromSle.getLoanBrokerID(); + auto const fromBuilderOpt = entryFromBuilder.getLoanBrokerID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLoanBrokerID"); + expectEqualField(expected, *fromBuilderOpt, "sfLoanBrokerID"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(AccountRootTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(AccountRoot{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(AccountRootTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(AccountRootBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(AccountRootTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const balanceValue = canonical_AMOUNT(); + auto const ownerCountValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + AccountRootBuilder builder{ + accountValue, + sequenceValue, + balanceValue, + ownerCountValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasAccountTxnID()); + EXPECT_FALSE(entry.getAccountTxnID().has_value()); + EXPECT_FALSE(entry.hasRegularKey()); + EXPECT_FALSE(entry.getRegularKey().has_value()); + EXPECT_FALSE(entry.hasEmailHash()); + EXPECT_FALSE(entry.getEmailHash().has_value()); + EXPECT_FALSE(entry.hasWalletLocator()); + EXPECT_FALSE(entry.getWalletLocator().has_value()); + EXPECT_FALSE(entry.hasWalletSize()); + EXPECT_FALSE(entry.getWalletSize().has_value()); + EXPECT_FALSE(entry.hasMessageKey()); + EXPECT_FALSE(entry.getMessageKey().has_value()); + EXPECT_FALSE(entry.hasTransferRate()); + EXPECT_FALSE(entry.getTransferRate().has_value()); + EXPECT_FALSE(entry.hasDomain()); + EXPECT_FALSE(entry.getDomain().has_value()); + EXPECT_FALSE(entry.hasTickSize()); + EXPECT_FALSE(entry.getTickSize().has_value()); + EXPECT_FALSE(entry.hasTicketCount()); + EXPECT_FALSE(entry.getTicketCount().has_value()); + EXPECT_FALSE(entry.hasNFTokenMinter()); + EXPECT_FALSE(entry.getNFTokenMinter().has_value()); + EXPECT_FALSE(entry.hasMintedNFTokens()); + EXPECT_FALSE(entry.getMintedNFTokens().has_value()); + EXPECT_FALSE(entry.hasBurnedNFTokens()); + EXPECT_FALSE(entry.getBurnedNFTokens().has_value()); + EXPECT_FALSE(entry.hasFirstNFTokenSequence()); + EXPECT_FALSE(entry.getFirstNFTokenSequence().has_value()); + EXPECT_FALSE(entry.hasAMMID()); + EXPECT_FALSE(entry.getAMMID().has_value()); + EXPECT_FALSE(entry.hasVaultID()); + EXPECT_FALSE(entry.getVaultID().has_value()); + EXPECT_FALSE(entry.hasLoanBrokerID()); + EXPECT_FALSE(entry.getLoanBrokerID().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/AmendmentsTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/AmendmentsTests.cpp new file mode 100644 index 0000000000..17da755a3d --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/AmendmentsTests.cpp @@ -0,0 +1,224 @@ +// Auto-generated unit tests for ledger entry Amendments + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(AmendmentsTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const amendmentsValue = canonical_VECTOR256(); + auto const majoritiesValue = canonical_ARRAY(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + AmendmentsBuilder builder{ + }; + + builder.setAmendments(amendmentsValue); + builder.setMajorities(majoritiesValue); + builder.setPreviousTxnID(previousTxnIDValue); + builder.setPreviousTxnLgrSeq(previousTxnLgrSeqValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = amendmentsValue; + auto const actualOpt = entry.getAmendments(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAmendments"); + EXPECT_TRUE(entry.hasAmendments()); + } + + { + auto const& expected = majoritiesValue; + auto const actualOpt = entry.getMajorities(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMajorities"); + EXPECT_TRUE(entry.hasMajorities()); + } + + { + auto const& expected = previousTxnIDValue; + auto const actualOpt = entry.getPreviousTxnID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnID"); + EXPECT_TRUE(entry.hasPreviousTxnID()); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actualOpt = entry.getPreviousTxnLgrSeq(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnLgrSeq"); + EXPECT_TRUE(entry.hasPreviousTxnLgrSeq()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(AmendmentsTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const amendmentsValue = canonical_VECTOR256(); + auto const majoritiesValue = canonical_ARRAY(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(Amendments::entryType, index); + + sle->at(sfAmendments) = amendmentsValue; + sle->setFieldArray(sfMajorities, majoritiesValue); + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + AmendmentsBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Amendments entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = amendmentsValue; + + auto const fromSleOpt = entryFromSle.getAmendments(); + auto const fromBuilderOpt = entryFromBuilder.getAmendments(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAmendments"); + expectEqualField(expected, *fromBuilderOpt, "sfAmendments"); + } + + { + auto const& expected = majoritiesValue; + + auto const fromSleOpt = entryFromSle.getMajorities(); + auto const fromBuilderOpt = entryFromBuilder.getMajorities(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMajorities"); + expectEqualField(expected, *fromBuilderOpt, "sfMajorities"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnID(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnID"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnLgrSeq(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(AmendmentsTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Amendments{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(AmendmentsTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(AmendmentsBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(AmendmentsTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + + AmendmentsBuilder builder{ + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasAmendments()); + EXPECT_FALSE(entry.getAmendments().has_value()); + EXPECT_FALSE(entry.hasMajorities()); + EXPECT_FALSE(entry.getMajorities().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnID()); + EXPECT_FALSE(entry.getPreviousTxnID().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnLgrSeq()); + EXPECT_FALSE(entry.getPreviousTxnLgrSeq().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/BridgeTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/BridgeTests.cpp new file mode 100644 index 0000000000..1d4cfc00b9 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/BridgeTests.cpp @@ -0,0 +1,341 @@ +// Auto-generated unit tests for ledger entry Bridge + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(BridgeTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const minAccountCreateAmountValue = canonical_AMOUNT(); + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const xChainAccountCreateCountValue = canonical_UINT64(); + auto const xChainAccountClaimCountValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + BridgeBuilder builder{ + accountValue, + signatureRewardValue, + xChainBridgeValue, + xChainClaimIDValue, + xChainAccountCreateCountValue, + xChainAccountClaimCountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setMinAccountCreateAmount(minAccountCreateAmountValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = entry.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + { + auto const& expected = xChainBridgeValue; + auto const actual = entry.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = entry.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + { + auto const& expected = xChainAccountCreateCountValue; + auto const actual = entry.getXChainAccountCreateCount(); + expectEqualField(expected, actual, "sfXChainAccountCreateCount"); + } + + { + auto const& expected = xChainAccountClaimCountValue; + auto const actual = entry.getXChainAccountClaimCount(); + expectEqualField(expected, actual, "sfXChainAccountClaimCount"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = minAccountCreateAmountValue; + auto const actualOpt = entry.getMinAccountCreateAmount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMinAccountCreateAmount"); + EXPECT_TRUE(entry.hasMinAccountCreateAmount()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(BridgeTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const minAccountCreateAmountValue = canonical_AMOUNT(); + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const xChainAccountCreateCountValue = canonical_UINT64(); + auto const xChainAccountClaimCountValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(Bridge::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfSignatureReward) = signatureRewardValue; + sle->at(sfMinAccountCreateAmount) = minAccountCreateAmountValue; + sle->at(sfXChainBridge) = xChainBridgeValue; + sle->at(sfXChainClaimID) = xChainClaimIDValue; + sle->at(sfXChainAccountCreateCount) = xChainAccountCreateCountValue; + sle->at(sfXChainAccountClaimCount) = xChainAccountClaimCountValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + BridgeBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Bridge entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = signatureRewardValue; + + auto const fromSle = entryFromSle.getSignatureReward(); + auto const fromBuilder = entryFromBuilder.getSignatureReward(); + + expectEqualField(expected, fromSle, "sfSignatureReward"); + expectEqualField(expected, fromBuilder, "sfSignatureReward"); + } + + { + auto const& expected = xChainBridgeValue; + + auto const fromSle = entryFromSle.getXChainBridge(); + auto const fromBuilder = entryFromBuilder.getXChainBridge(); + + expectEqualField(expected, fromSle, "sfXChainBridge"); + expectEqualField(expected, fromBuilder, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + + auto const fromSle = entryFromSle.getXChainClaimID(); + auto const fromBuilder = entryFromBuilder.getXChainClaimID(); + + expectEqualField(expected, fromSle, "sfXChainClaimID"); + expectEqualField(expected, fromBuilder, "sfXChainClaimID"); + } + + { + auto const& expected = xChainAccountCreateCountValue; + + auto const fromSle = entryFromSle.getXChainAccountCreateCount(); + auto const fromBuilder = entryFromBuilder.getXChainAccountCreateCount(); + + expectEqualField(expected, fromSle, "sfXChainAccountCreateCount"); + expectEqualField(expected, fromBuilder, "sfXChainAccountCreateCount"); + } + + { + auto const& expected = xChainAccountClaimCountValue; + + auto const fromSle = entryFromSle.getXChainAccountClaimCount(); + auto const fromBuilder = entryFromBuilder.getXChainAccountClaimCount(); + + expectEqualField(expected, fromSle, "sfXChainAccountClaimCount"); + expectEqualField(expected, fromBuilder, "sfXChainAccountClaimCount"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = minAccountCreateAmountValue; + + auto const fromSleOpt = entryFromSle.getMinAccountCreateAmount(); + auto const fromBuilderOpt = entryFromBuilder.getMinAccountCreateAmount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMinAccountCreateAmount"); + expectEqualField(expected, *fromBuilderOpt, "sfMinAccountCreateAmount"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(BridgeTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Bridge{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(BridgeTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(BridgeBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(BridgeTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const xChainAccountCreateCountValue = canonical_UINT64(); + auto const xChainAccountClaimCountValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + BridgeBuilder builder{ + accountValue, + signatureRewardValue, + xChainBridgeValue, + xChainClaimIDValue, + xChainAccountCreateCountValue, + xChainAccountClaimCountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasMinAccountCreateAmount()); + EXPECT_FALSE(entry.getMinAccountCreateAmount().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/CheckTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/CheckTests.cpp new file mode 100644 index 0000000000..f14b4fb4e4 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/CheckTests.cpp @@ -0,0 +1,400 @@ +// Auto-generated unit tests for ledger entry Check + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(CheckTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const destinationNodeValue = canonical_UINT64(); + auto const expirationValue = canonical_UINT32(); + auto const invoiceIDValue = canonical_UINT256(); + auto const sourceTagValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + CheckBuilder builder{ + accountValue, + destinationValue, + sendMaxValue, + sequenceValue, + ownerNodeValue, + destinationNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setExpiration(expirationValue); + builder.setInvoiceID(invoiceIDValue); + builder.setSourceTag(sourceTagValue); + builder.setDestinationTag(destinationTagValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = destinationValue; + auto const actual = entry.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = sendMaxValue; + auto const actual = entry.getSendMax(); + expectEqualField(expected, actual, "sfSendMax"); + } + + { + auto const& expected = sequenceValue; + auto const actual = entry.getSequence(); + expectEqualField(expected, actual, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = destinationNodeValue; + auto const actual = entry.getDestinationNode(); + expectEqualField(expected, actual, "sfDestinationNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = entry.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(entry.hasExpiration()); + } + + { + auto const& expected = invoiceIDValue; + auto const actualOpt = entry.getInvoiceID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfInvoiceID"); + EXPECT_TRUE(entry.hasInvoiceID()); + } + + { + auto const& expected = sourceTagValue; + auto const actualOpt = entry.getSourceTag(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfSourceTag"); + EXPECT_TRUE(entry.hasSourceTag()); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = entry.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + EXPECT_TRUE(entry.hasDestinationTag()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(CheckTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const destinationNodeValue = canonical_UINT64(); + auto const expirationValue = canonical_UINT32(); + auto const invoiceIDValue = canonical_UINT256(); + auto const sourceTagValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(Check::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfDestination) = destinationValue; + sle->at(sfSendMax) = sendMaxValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfDestinationNode) = destinationNodeValue; + sle->at(sfExpiration) = expirationValue; + sle->at(sfInvoiceID) = invoiceIDValue; + sle->at(sfSourceTag) = sourceTagValue; + sle->at(sfDestinationTag) = destinationTagValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + CheckBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Check entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = destinationValue; + + auto const fromSle = entryFromSle.getDestination(); + auto const fromBuilder = entryFromBuilder.getDestination(); + + expectEqualField(expected, fromSle, "sfDestination"); + expectEqualField(expected, fromBuilder, "sfDestination"); + } + + { + auto const& expected = sendMaxValue; + + auto const fromSle = entryFromSle.getSendMax(); + auto const fromBuilder = entryFromBuilder.getSendMax(); + + expectEqualField(expected, fromSle, "sfSendMax"); + expectEqualField(expected, fromBuilder, "sfSendMax"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSle = entryFromSle.getSequence(); + auto const fromBuilder = entryFromBuilder.getSequence(); + + expectEqualField(expected, fromSle, "sfSequence"); + expectEqualField(expected, fromBuilder, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = destinationNodeValue; + + auto const fromSle = entryFromSle.getDestinationNode(); + auto const fromBuilder = entryFromBuilder.getDestinationNode(); + + expectEqualField(expected, fromSle, "sfDestinationNode"); + expectEqualField(expected, fromBuilder, "sfDestinationNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = expirationValue; + + auto const fromSleOpt = entryFromSle.getExpiration(); + auto const fromBuilderOpt = entryFromBuilder.getExpiration(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfExpiration"); + expectEqualField(expected, *fromBuilderOpt, "sfExpiration"); + } + + { + auto const& expected = invoiceIDValue; + + auto const fromSleOpt = entryFromSle.getInvoiceID(); + auto const fromBuilderOpt = entryFromBuilder.getInvoiceID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfInvoiceID"); + expectEqualField(expected, *fromBuilderOpt, "sfInvoiceID"); + } + + { + auto const& expected = sourceTagValue; + + auto const fromSleOpt = entryFromSle.getSourceTag(); + auto const fromBuilderOpt = entryFromBuilder.getSourceTag(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfSourceTag"); + expectEqualField(expected, *fromBuilderOpt, "sfSourceTag"); + } + + { + auto const& expected = destinationTagValue; + + auto const fromSleOpt = entryFromSle.getDestinationTag(); + auto const fromBuilderOpt = entryFromBuilder.getDestinationTag(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDestinationTag"); + expectEqualField(expected, *fromBuilderOpt, "sfDestinationTag"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(CheckTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Check{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(CheckTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(CheckBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(CheckTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const destinationNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + CheckBuilder builder{ + accountValue, + destinationValue, + sendMaxValue, + sequenceValue, + ownerNodeValue, + destinationNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasExpiration()); + EXPECT_FALSE(entry.getExpiration().has_value()); + EXPECT_FALSE(entry.hasInvoiceID()); + EXPECT_FALSE(entry.getInvoiceID().has_value()); + EXPECT_FALSE(entry.hasSourceTag()); + EXPECT_FALSE(entry.getSourceTag().has_value()); + EXPECT_FALSE(entry.hasDestinationTag()); + EXPECT_FALSE(entry.getDestinationTag().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/CredentialTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/CredentialTests.cpp new file mode 100644 index 0000000000..4fbf6d2a26 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/CredentialTests.cpp @@ -0,0 +1,329 @@ +// Auto-generated unit tests for ledger entry Credential + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(CredentialTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const subjectValue = canonical_ACCOUNT(); + auto const issuerValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + auto const expirationValue = canonical_UINT32(); + auto const uRIValue = canonical_VL(); + auto const issuerNodeValue = canonical_UINT64(); + auto const subjectNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + CredentialBuilder builder{ + subjectValue, + issuerValue, + credentialTypeValue, + issuerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setExpiration(expirationValue); + builder.setURI(uRIValue); + builder.setSubjectNode(subjectNodeValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = subjectValue; + auto const actual = entry.getSubject(); + expectEqualField(expected, actual, "sfSubject"); + } + + { + auto const& expected = issuerValue; + auto const actual = entry.getIssuer(); + expectEqualField(expected, actual, "sfIssuer"); + } + + { + auto const& expected = credentialTypeValue; + auto const actual = entry.getCredentialType(); + expectEqualField(expected, actual, "sfCredentialType"); + } + + { + auto const& expected = issuerNodeValue; + auto const actual = entry.getIssuerNode(); + expectEqualField(expected, actual, "sfIssuerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = entry.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(entry.hasExpiration()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = entry.getURI(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(entry.hasURI()); + } + + { + auto const& expected = subjectNodeValue; + auto const actualOpt = entry.getSubjectNode(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfSubjectNode"); + EXPECT_TRUE(entry.hasSubjectNode()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(CredentialTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const subjectValue = canonical_ACCOUNT(); + auto const issuerValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + auto const expirationValue = canonical_UINT32(); + auto const uRIValue = canonical_VL(); + auto const issuerNodeValue = canonical_UINT64(); + auto const subjectNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(Credential::entryType, index); + + sle->at(sfSubject) = subjectValue; + sle->at(sfIssuer) = issuerValue; + sle->at(sfCredentialType) = credentialTypeValue; + sle->at(sfExpiration) = expirationValue; + sle->at(sfURI) = uRIValue; + sle->at(sfIssuerNode) = issuerNodeValue; + sle->at(sfSubjectNode) = subjectNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + CredentialBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Credential entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = subjectValue; + + auto const fromSle = entryFromSle.getSubject(); + auto const fromBuilder = entryFromBuilder.getSubject(); + + expectEqualField(expected, fromSle, "sfSubject"); + expectEqualField(expected, fromBuilder, "sfSubject"); + } + + { + auto const& expected = issuerValue; + + auto const fromSle = entryFromSle.getIssuer(); + auto const fromBuilder = entryFromBuilder.getIssuer(); + + expectEqualField(expected, fromSle, "sfIssuer"); + expectEqualField(expected, fromBuilder, "sfIssuer"); + } + + { + auto const& expected = credentialTypeValue; + + auto const fromSle = entryFromSle.getCredentialType(); + auto const fromBuilder = entryFromBuilder.getCredentialType(); + + expectEqualField(expected, fromSle, "sfCredentialType"); + expectEqualField(expected, fromBuilder, "sfCredentialType"); + } + + { + auto const& expected = issuerNodeValue; + + auto const fromSle = entryFromSle.getIssuerNode(); + auto const fromBuilder = entryFromBuilder.getIssuerNode(); + + expectEqualField(expected, fromSle, "sfIssuerNode"); + expectEqualField(expected, fromBuilder, "sfIssuerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = expirationValue; + + auto const fromSleOpt = entryFromSle.getExpiration(); + auto const fromBuilderOpt = entryFromBuilder.getExpiration(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfExpiration"); + expectEqualField(expected, *fromBuilderOpt, "sfExpiration"); + } + + { + auto const& expected = uRIValue; + + auto const fromSleOpt = entryFromSle.getURI(); + auto const fromBuilderOpt = entryFromBuilder.getURI(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfURI"); + expectEqualField(expected, *fromBuilderOpt, "sfURI"); + } + + { + auto const& expected = subjectNodeValue; + + auto const fromSleOpt = entryFromSle.getSubjectNode(); + auto const fromBuilderOpt = entryFromBuilder.getSubjectNode(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfSubjectNode"); + expectEqualField(expected, *fromBuilderOpt, "sfSubjectNode"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(CredentialTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Credential{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(CredentialTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(CredentialBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(CredentialTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const subjectValue = canonical_ACCOUNT(); + auto const issuerValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + auto const issuerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + CredentialBuilder builder{ + subjectValue, + issuerValue, + credentialTypeValue, + issuerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasExpiration()); + EXPECT_FALSE(entry.getExpiration().has_value()); + EXPECT_FALSE(entry.hasURI()); + EXPECT_FALSE(entry.getURI().has_value()); + EXPECT_FALSE(entry.hasSubjectNode()); + EXPECT_FALSE(entry.getSubjectNode().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/DIDTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/DIDTests.cpp new file mode 100644 index 0000000000..29d49a47f2 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/DIDTests.cpp @@ -0,0 +1,285 @@ +// Auto-generated unit tests for ledger entry DID + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(DIDTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const dIDDocumentValue = canonical_VL(); + auto const uRIValue = canonical_VL(); + auto const dataValue = canonical_VL(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + DIDBuilder builder{ + accountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setDIDDocument(dIDDocumentValue); + builder.setURI(uRIValue); + builder.setData(dataValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = dIDDocumentValue; + auto const actualOpt = entry.getDIDDocument(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDIDDocument"); + EXPECT_TRUE(entry.hasDIDDocument()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = entry.getURI(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(entry.hasURI()); + } + + { + auto const& expected = dataValue; + auto const actualOpt = entry.getData(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfData"); + EXPECT_TRUE(entry.hasData()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(DIDTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const dIDDocumentValue = canonical_VL(); + auto const uRIValue = canonical_VL(); + auto const dataValue = canonical_VL(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(DID::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfDIDDocument) = dIDDocumentValue; + sle->at(sfURI) = uRIValue; + sle->at(sfData) = dataValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + DIDBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + DID entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = dIDDocumentValue; + + auto const fromSleOpt = entryFromSle.getDIDDocument(); + auto const fromBuilderOpt = entryFromBuilder.getDIDDocument(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDIDDocument"); + expectEqualField(expected, *fromBuilderOpt, "sfDIDDocument"); + } + + { + auto const& expected = uRIValue; + + auto const fromSleOpt = entryFromSle.getURI(); + auto const fromBuilderOpt = entryFromBuilder.getURI(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfURI"); + expectEqualField(expected, *fromBuilderOpt, "sfURI"); + } + + { + auto const& expected = dataValue; + + auto const fromSleOpt = entryFromSle.getData(); + auto const fromBuilderOpt = entryFromBuilder.getData(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfData"); + expectEqualField(expected, *fromBuilderOpt, "sfData"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(DIDTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(DID{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(DIDTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(DIDBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(DIDTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + DIDBuilder builder{ + accountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasDIDDocument()); + EXPECT_FALSE(entry.getDIDDocument().has_value()); + EXPECT_FALSE(entry.hasURI()); + EXPECT_FALSE(entry.getURI().has_value()); + EXPECT_FALSE(entry.hasData()); + EXPECT_FALSE(entry.getData().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/DelegateTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/DelegateTests.cpp new file mode 100644 index 0000000000..68d0e6c08e --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/DelegateTests.cpp @@ -0,0 +1,223 @@ +// Auto-generated unit tests for ledger entry Delegate + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(DelegateTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const authorizeValue = canonical_ACCOUNT(); + auto const permissionsValue = canonical_ARRAY(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + DelegateBuilder builder{ + accountValue, + authorizeValue, + permissionsValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = authorizeValue; + auto const actual = entry.getAuthorize(); + expectEqualField(expected, actual, "sfAuthorize"); + } + + { + auto const& expected = permissionsValue; + auto const actual = entry.getPermissions(); + expectEqualField(expected, actual, "sfPermissions"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(DelegateTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const authorizeValue = canonical_ACCOUNT(); + auto const permissionsValue = canonical_ARRAY(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(Delegate::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfAuthorize) = authorizeValue; + sle->setFieldArray(sfPermissions, permissionsValue); + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + DelegateBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Delegate entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = authorizeValue; + + auto const fromSle = entryFromSle.getAuthorize(); + auto const fromBuilder = entryFromBuilder.getAuthorize(); + + expectEqualField(expected, fromSle, "sfAuthorize"); + expectEqualField(expected, fromBuilder, "sfAuthorize"); + } + + { + auto const& expected = permissionsValue; + + auto const fromSle = entryFromSle.getPermissions(); + auto const fromBuilder = entryFromBuilder.getPermissions(); + + expectEqualField(expected, fromSle, "sfPermissions"); + expectEqualField(expected, fromBuilder, "sfPermissions"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(DelegateTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Delegate{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(DelegateTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(DelegateBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/DepositPreauthTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/DepositPreauthTests.cpp new file mode 100644 index 0000000000..9e6144d5b9 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/DepositPreauthTests.cpp @@ -0,0 +1,258 @@ +// Auto-generated unit tests for ledger entry DepositPreauth + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(DepositPreauthTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const authorizeValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const authorizeCredentialsValue = canonical_ARRAY(); + + DepositPreauthBuilder builder{ + accountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setAuthorize(authorizeValue); + builder.setAuthorizeCredentials(authorizeCredentialsValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = authorizeValue; + auto const actualOpt = entry.getAuthorize(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAuthorize"); + EXPECT_TRUE(entry.hasAuthorize()); + } + + { + auto const& expected = authorizeCredentialsValue; + auto const actualOpt = entry.getAuthorizeCredentials(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAuthorizeCredentials"); + EXPECT_TRUE(entry.hasAuthorizeCredentials()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(DepositPreauthTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const authorizeValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const authorizeCredentialsValue = canonical_ARRAY(); + + auto sle = std::make_shared(DepositPreauth::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfAuthorize) = authorizeValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->setFieldArray(sfAuthorizeCredentials, authorizeCredentialsValue); + + DepositPreauthBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + DepositPreauth entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = authorizeValue; + + auto const fromSleOpt = entryFromSle.getAuthorize(); + auto const fromBuilderOpt = entryFromBuilder.getAuthorize(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAuthorize"); + expectEqualField(expected, *fromBuilderOpt, "sfAuthorize"); + } + + { + auto const& expected = authorizeCredentialsValue; + + auto const fromSleOpt = entryFromSle.getAuthorizeCredentials(); + auto const fromBuilderOpt = entryFromBuilder.getAuthorizeCredentials(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAuthorizeCredentials"); + expectEqualField(expected, *fromBuilderOpt, "sfAuthorizeCredentials"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(DepositPreauthTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(DepositPreauth{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(DepositPreauthTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(DepositPreauthBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(DepositPreauthTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + DepositPreauthBuilder builder{ + accountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasAuthorize()); + EXPECT_FALSE(entry.getAuthorize().has_value()); + EXPECT_FALSE(entry.hasAuthorizeCredentials()); + EXPECT_FALSE(entry.getAuthorizeCredentials().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/DirectoryNodeTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/DirectoryNodeTests.cpp new file mode 100644 index 0000000000..d1b6edc704 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/DirectoryNodeTests.cpp @@ -0,0 +1,484 @@ +// Auto-generated unit tests for ledger entry DirectoryNode + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(DirectoryNodeTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const takerPaysCurrencyValue = canonical_UINT160(); + auto const takerPaysIssuerValue = canonical_UINT160(); + auto const takerGetsCurrencyValue = canonical_UINT160(); + auto const takerGetsIssuerValue = canonical_UINT160(); + auto const exchangeRateValue = canonical_UINT64(); + auto const indexesValue = canonical_VECTOR256(); + auto const rootIndexValue = canonical_UINT256(); + auto const indexNextValue = canonical_UINT64(); + auto const indexPreviousValue = canonical_UINT64(); + auto const nFTokenIDValue = canonical_UINT256(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + + DirectoryNodeBuilder builder{ + indexesValue, + rootIndexValue + }; + + builder.setOwner(ownerValue); + builder.setTakerPaysCurrency(takerPaysCurrencyValue); + builder.setTakerPaysIssuer(takerPaysIssuerValue); + builder.setTakerGetsCurrency(takerGetsCurrencyValue); + builder.setTakerGetsIssuer(takerGetsIssuerValue); + builder.setExchangeRate(exchangeRateValue); + builder.setIndexNext(indexNextValue); + builder.setIndexPrevious(indexPreviousValue); + builder.setNFTokenID(nFTokenIDValue); + builder.setPreviousTxnID(previousTxnIDValue); + builder.setPreviousTxnLgrSeq(previousTxnLgrSeqValue); + builder.setDomainID(domainIDValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = indexesValue; + auto const actual = entry.getIndexes(); + expectEqualField(expected, actual, "sfIndexes"); + } + + { + auto const& expected = rootIndexValue; + auto const actual = entry.getRootIndex(); + expectEqualField(expected, actual, "sfRootIndex"); + } + + { + auto const& expected = ownerValue; + auto const actualOpt = entry.getOwner(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfOwner"); + EXPECT_TRUE(entry.hasOwner()); + } + + { + auto const& expected = takerPaysCurrencyValue; + auto const actualOpt = entry.getTakerPaysCurrency(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTakerPaysCurrency"); + EXPECT_TRUE(entry.hasTakerPaysCurrency()); + } + + { + auto const& expected = takerPaysIssuerValue; + auto const actualOpt = entry.getTakerPaysIssuer(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTakerPaysIssuer"); + EXPECT_TRUE(entry.hasTakerPaysIssuer()); + } + + { + auto const& expected = takerGetsCurrencyValue; + auto const actualOpt = entry.getTakerGetsCurrency(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTakerGetsCurrency"); + EXPECT_TRUE(entry.hasTakerGetsCurrency()); + } + + { + auto const& expected = takerGetsIssuerValue; + auto const actualOpt = entry.getTakerGetsIssuer(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTakerGetsIssuer"); + EXPECT_TRUE(entry.hasTakerGetsIssuer()); + } + + { + auto const& expected = exchangeRateValue; + auto const actualOpt = entry.getExchangeRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfExchangeRate"); + EXPECT_TRUE(entry.hasExchangeRate()); + } + + { + auto const& expected = indexNextValue; + auto const actualOpt = entry.getIndexNext(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfIndexNext"); + EXPECT_TRUE(entry.hasIndexNext()); + } + + { + auto const& expected = indexPreviousValue; + auto const actualOpt = entry.getIndexPrevious(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfIndexPrevious"); + EXPECT_TRUE(entry.hasIndexPrevious()); + } + + { + auto const& expected = nFTokenIDValue; + auto const actualOpt = entry.getNFTokenID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfNFTokenID"); + EXPECT_TRUE(entry.hasNFTokenID()); + } + + { + auto const& expected = previousTxnIDValue; + auto const actualOpt = entry.getPreviousTxnID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnID"); + EXPECT_TRUE(entry.hasPreviousTxnID()); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actualOpt = entry.getPreviousTxnLgrSeq(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnLgrSeq"); + EXPECT_TRUE(entry.hasPreviousTxnLgrSeq()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = entry.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(entry.hasDomainID()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(DirectoryNodeTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const takerPaysCurrencyValue = canonical_UINT160(); + auto const takerPaysIssuerValue = canonical_UINT160(); + auto const takerGetsCurrencyValue = canonical_UINT160(); + auto const takerGetsIssuerValue = canonical_UINT160(); + auto const exchangeRateValue = canonical_UINT64(); + auto const indexesValue = canonical_VECTOR256(); + auto const rootIndexValue = canonical_UINT256(); + auto const indexNextValue = canonical_UINT64(); + auto const indexPreviousValue = canonical_UINT64(); + auto const nFTokenIDValue = canonical_UINT256(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + + auto sle = std::make_shared(DirectoryNode::entryType, index); + + sle->at(sfOwner) = ownerValue; + sle->at(sfTakerPaysCurrency) = takerPaysCurrencyValue; + sle->at(sfTakerPaysIssuer) = takerPaysIssuerValue; + sle->at(sfTakerGetsCurrency) = takerGetsCurrencyValue; + sle->at(sfTakerGetsIssuer) = takerGetsIssuerValue; + sle->at(sfExchangeRate) = exchangeRateValue; + sle->at(sfIndexes) = indexesValue; + sle->at(sfRootIndex) = rootIndexValue; + sle->at(sfIndexNext) = indexNextValue; + sle->at(sfIndexPrevious) = indexPreviousValue; + sle->at(sfNFTokenID) = nFTokenIDValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfDomainID) = domainIDValue; + + DirectoryNodeBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + DirectoryNode entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = indexesValue; + + auto const fromSle = entryFromSle.getIndexes(); + auto const fromBuilder = entryFromBuilder.getIndexes(); + + expectEqualField(expected, fromSle, "sfIndexes"); + expectEqualField(expected, fromBuilder, "sfIndexes"); + } + + { + auto const& expected = rootIndexValue; + + auto const fromSle = entryFromSle.getRootIndex(); + auto const fromBuilder = entryFromBuilder.getRootIndex(); + + expectEqualField(expected, fromSle, "sfRootIndex"); + expectEqualField(expected, fromBuilder, "sfRootIndex"); + } + + { + auto const& expected = ownerValue; + + auto const fromSleOpt = entryFromSle.getOwner(); + auto const fromBuilderOpt = entryFromBuilder.getOwner(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfOwner"); + expectEqualField(expected, *fromBuilderOpt, "sfOwner"); + } + + { + auto const& expected = takerPaysCurrencyValue; + + auto const fromSleOpt = entryFromSle.getTakerPaysCurrency(); + auto const fromBuilderOpt = entryFromBuilder.getTakerPaysCurrency(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTakerPaysCurrency"); + expectEqualField(expected, *fromBuilderOpt, "sfTakerPaysCurrency"); + } + + { + auto const& expected = takerPaysIssuerValue; + + auto const fromSleOpt = entryFromSle.getTakerPaysIssuer(); + auto const fromBuilderOpt = entryFromBuilder.getTakerPaysIssuer(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTakerPaysIssuer"); + expectEqualField(expected, *fromBuilderOpt, "sfTakerPaysIssuer"); + } + + { + auto const& expected = takerGetsCurrencyValue; + + auto const fromSleOpt = entryFromSle.getTakerGetsCurrency(); + auto const fromBuilderOpt = entryFromBuilder.getTakerGetsCurrency(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTakerGetsCurrency"); + expectEqualField(expected, *fromBuilderOpt, "sfTakerGetsCurrency"); + } + + { + auto const& expected = takerGetsIssuerValue; + + auto const fromSleOpt = entryFromSle.getTakerGetsIssuer(); + auto const fromBuilderOpt = entryFromBuilder.getTakerGetsIssuer(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTakerGetsIssuer"); + expectEqualField(expected, *fromBuilderOpt, "sfTakerGetsIssuer"); + } + + { + auto const& expected = exchangeRateValue; + + auto const fromSleOpt = entryFromSle.getExchangeRate(); + auto const fromBuilderOpt = entryFromBuilder.getExchangeRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfExchangeRate"); + expectEqualField(expected, *fromBuilderOpt, "sfExchangeRate"); + } + + { + auto const& expected = indexNextValue; + + auto const fromSleOpt = entryFromSle.getIndexNext(); + auto const fromBuilderOpt = entryFromBuilder.getIndexNext(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfIndexNext"); + expectEqualField(expected, *fromBuilderOpt, "sfIndexNext"); + } + + { + auto const& expected = indexPreviousValue; + + auto const fromSleOpt = entryFromSle.getIndexPrevious(); + auto const fromBuilderOpt = entryFromBuilder.getIndexPrevious(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfIndexPrevious"); + expectEqualField(expected, *fromBuilderOpt, "sfIndexPrevious"); + } + + { + auto const& expected = nFTokenIDValue; + + auto const fromSleOpt = entryFromSle.getNFTokenID(); + auto const fromBuilderOpt = entryFromBuilder.getNFTokenID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfNFTokenID"); + expectEqualField(expected, *fromBuilderOpt, "sfNFTokenID"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnID(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnID"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnLgrSeq(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = domainIDValue; + + auto const fromSleOpt = entryFromSle.getDomainID(); + auto const fromBuilderOpt = entryFromBuilder.getDomainID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDomainID"); + expectEqualField(expected, *fromBuilderOpt, "sfDomainID"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(DirectoryNodeTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(DirectoryNode{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(DirectoryNodeTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(DirectoryNodeBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(DirectoryNodeTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const indexesValue = canonical_VECTOR256(); + auto const rootIndexValue = canonical_UINT256(); + + DirectoryNodeBuilder builder{ + indexesValue, + rootIndexValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasOwner()); + EXPECT_FALSE(entry.getOwner().has_value()); + EXPECT_FALSE(entry.hasTakerPaysCurrency()); + EXPECT_FALSE(entry.getTakerPaysCurrency().has_value()); + EXPECT_FALSE(entry.hasTakerPaysIssuer()); + EXPECT_FALSE(entry.getTakerPaysIssuer().has_value()); + EXPECT_FALSE(entry.hasTakerGetsCurrency()); + EXPECT_FALSE(entry.getTakerGetsCurrency().has_value()); + EXPECT_FALSE(entry.hasTakerGetsIssuer()); + EXPECT_FALSE(entry.getTakerGetsIssuer().has_value()); + EXPECT_FALSE(entry.hasExchangeRate()); + EXPECT_FALSE(entry.getExchangeRate().has_value()); + EXPECT_FALSE(entry.hasIndexNext()); + EXPECT_FALSE(entry.getIndexNext().has_value()); + EXPECT_FALSE(entry.hasIndexPrevious()); + EXPECT_FALSE(entry.getIndexPrevious().has_value()); + EXPECT_FALSE(entry.hasNFTokenID()); + EXPECT_FALSE(entry.getNFTokenID().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnID()); + EXPECT_FALSE(entry.getPreviousTxnID().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnLgrSeq()); + EXPECT_FALSE(entry.getPreviousTxnLgrSeq().has_value()); + EXPECT_FALSE(entry.hasDomainID()); + EXPECT_FALSE(entry.getDomainID().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/EscrowTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/EscrowTests.cpp new file mode 100644 index 0000000000..2dbb450e28 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/EscrowTests.cpp @@ -0,0 +1,491 @@ +// Auto-generated unit tests for ledger entry Escrow + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(EscrowTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const conditionValue = canonical_VL(); + auto const cancelAfterValue = canonical_UINT32(); + auto const finishAfterValue = canonical_UINT32(); + auto const sourceTagValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const destinationNodeValue = canonical_UINT64(); + auto const transferRateValue = canonical_UINT32(); + auto const issuerNodeValue = canonical_UINT64(); + + EscrowBuilder builder{ + accountValue, + destinationValue, + amountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setSequence(sequenceValue); + builder.setCondition(conditionValue); + builder.setCancelAfter(cancelAfterValue); + builder.setFinishAfter(finishAfterValue); + builder.setSourceTag(sourceTagValue); + builder.setDestinationTag(destinationTagValue); + builder.setDestinationNode(destinationNodeValue); + builder.setTransferRate(transferRateValue); + builder.setIssuerNode(issuerNodeValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = destinationValue; + auto const actual = entry.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = entry.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + auto const actualOpt = entry.getSequence(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfSequence"); + EXPECT_TRUE(entry.hasSequence()); + } + + { + auto const& expected = conditionValue; + auto const actualOpt = entry.getCondition(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfCondition"); + EXPECT_TRUE(entry.hasCondition()); + } + + { + auto const& expected = cancelAfterValue; + auto const actualOpt = entry.getCancelAfter(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfCancelAfter"); + EXPECT_TRUE(entry.hasCancelAfter()); + } + + { + auto const& expected = finishAfterValue; + auto const actualOpt = entry.getFinishAfter(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfFinishAfter"); + EXPECT_TRUE(entry.hasFinishAfter()); + } + + { + auto const& expected = sourceTagValue; + auto const actualOpt = entry.getSourceTag(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfSourceTag"); + EXPECT_TRUE(entry.hasSourceTag()); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = entry.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + EXPECT_TRUE(entry.hasDestinationTag()); + } + + { + auto const& expected = destinationNodeValue; + auto const actualOpt = entry.getDestinationNode(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDestinationNode"); + EXPECT_TRUE(entry.hasDestinationNode()); + } + + { + auto const& expected = transferRateValue; + auto const actualOpt = entry.getTransferRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTransferRate"); + EXPECT_TRUE(entry.hasTransferRate()); + } + + { + auto const& expected = issuerNodeValue; + auto const actualOpt = entry.getIssuerNode(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfIssuerNode"); + EXPECT_TRUE(entry.hasIssuerNode()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(EscrowTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const conditionValue = canonical_VL(); + auto const cancelAfterValue = canonical_UINT32(); + auto const finishAfterValue = canonical_UINT32(); + auto const sourceTagValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const destinationNodeValue = canonical_UINT64(); + auto const transferRateValue = canonical_UINT32(); + auto const issuerNodeValue = canonical_UINT64(); + + auto sle = std::make_shared(Escrow::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfDestination) = destinationValue; + sle->at(sfAmount) = amountValue; + sle->at(sfCondition) = conditionValue; + sle->at(sfCancelAfter) = cancelAfterValue; + sle->at(sfFinishAfter) = finishAfterValue; + sle->at(sfSourceTag) = sourceTagValue; + sle->at(sfDestinationTag) = destinationTagValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfDestinationNode) = destinationNodeValue; + sle->at(sfTransferRate) = transferRateValue; + sle->at(sfIssuerNode) = issuerNodeValue; + + EscrowBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Escrow entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = destinationValue; + + auto const fromSle = entryFromSle.getDestination(); + auto const fromBuilder = entryFromBuilder.getDestination(); + + expectEqualField(expected, fromSle, "sfDestination"); + expectEqualField(expected, fromBuilder, "sfDestination"); + } + + { + auto const& expected = amountValue; + + auto const fromSle = entryFromSle.getAmount(); + auto const fromBuilder = entryFromBuilder.getAmount(); + + expectEqualField(expected, fromSle, "sfAmount"); + expectEqualField(expected, fromBuilder, "sfAmount"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSleOpt = entryFromSle.getSequence(); + auto const fromBuilderOpt = entryFromBuilder.getSequence(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfSequence"); + expectEqualField(expected, *fromBuilderOpt, "sfSequence"); + } + + { + auto const& expected = conditionValue; + + auto const fromSleOpt = entryFromSle.getCondition(); + auto const fromBuilderOpt = entryFromBuilder.getCondition(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfCondition"); + expectEqualField(expected, *fromBuilderOpt, "sfCondition"); + } + + { + auto const& expected = cancelAfterValue; + + auto const fromSleOpt = entryFromSle.getCancelAfter(); + auto const fromBuilderOpt = entryFromBuilder.getCancelAfter(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfCancelAfter"); + expectEqualField(expected, *fromBuilderOpt, "sfCancelAfter"); + } + + { + auto const& expected = finishAfterValue; + + auto const fromSleOpt = entryFromSle.getFinishAfter(); + auto const fromBuilderOpt = entryFromBuilder.getFinishAfter(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfFinishAfter"); + expectEqualField(expected, *fromBuilderOpt, "sfFinishAfter"); + } + + { + auto const& expected = sourceTagValue; + + auto const fromSleOpt = entryFromSle.getSourceTag(); + auto const fromBuilderOpt = entryFromBuilder.getSourceTag(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfSourceTag"); + expectEqualField(expected, *fromBuilderOpt, "sfSourceTag"); + } + + { + auto const& expected = destinationTagValue; + + auto const fromSleOpt = entryFromSle.getDestinationTag(); + auto const fromBuilderOpt = entryFromBuilder.getDestinationTag(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDestinationTag"); + expectEqualField(expected, *fromBuilderOpt, "sfDestinationTag"); + } + + { + auto const& expected = destinationNodeValue; + + auto const fromSleOpt = entryFromSle.getDestinationNode(); + auto const fromBuilderOpt = entryFromBuilder.getDestinationNode(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDestinationNode"); + expectEqualField(expected, *fromBuilderOpt, "sfDestinationNode"); + } + + { + auto const& expected = transferRateValue; + + auto const fromSleOpt = entryFromSle.getTransferRate(); + auto const fromBuilderOpt = entryFromBuilder.getTransferRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTransferRate"); + expectEqualField(expected, *fromBuilderOpt, "sfTransferRate"); + } + + { + auto const& expected = issuerNodeValue; + + auto const fromSleOpt = entryFromSle.getIssuerNode(); + auto const fromBuilderOpt = entryFromBuilder.getIssuerNode(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfIssuerNode"); + expectEqualField(expected, *fromBuilderOpt, "sfIssuerNode"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(EscrowTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Escrow{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(EscrowTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(EscrowBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(EscrowTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + EscrowBuilder builder{ + accountValue, + destinationValue, + amountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasSequence()); + EXPECT_FALSE(entry.getSequence().has_value()); + EXPECT_FALSE(entry.hasCondition()); + EXPECT_FALSE(entry.getCondition().has_value()); + EXPECT_FALSE(entry.hasCancelAfter()); + EXPECT_FALSE(entry.getCancelAfter().has_value()); + EXPECT_FALSE(entry.hasFinishAfter()); + EXPECT_FALSE(entry.getFinishAfter().has_value()); + EXPECT_FALSE(entry.hasSourceTag()); + EXPECT_FALSE(entry.getSourceTag().has_value()); + EXPECT_FALSE(entry.hasDestinationTag()); + EXPECT_FALSE(entry.getDestinationTag().has_value()); + EXPECT_FALSE(entry.hasDestinationNode()); + EXPECT_FALSE(entry.getDestinationNode().has_value()); + EXPECT_FALSE(entry.hasTransferRate()); + EXPECT_FALSE(entry.getTransferRate().has_value()); + EXPECT_FALSE(entry.hasIssuerNode()); + EXPECT_FALSE(entry.getIssuerNode().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/FeeSettingsTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/FeeSettingsTests.cpp new file mode 100644 index 0000000000..479d0c56c6 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/FeeSettingsTests.cpp @@ -0,0 +1,359 @@ +// Auto-generated unit tests for ledger entry FeeSettings + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(FeeSettingsTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const baseFeeValue = canonical_UINT64(); + auto const referenceFeeUnitsValue = canonical_UINT32(); + auto const reserveBaseValue = canonical_UINT32(); + auto const reserveIncrementValue = canonical_UINT32(); + auto const baseFeeDropsValue = canonical_AMOUNT(); + auto const reserveBaseDropsValue = canonical_AMOUNT(); + auto const reserveIncrementDropsValue = canonical_AMOUNT(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + FeeSettingsBuilder builder{ + }; + + builder.setBaseFee(baseFeeValue); + builder.setReferenceFeeUnits(referenceFeeUnitsValue); + builder.setReserveBase(reserveBaseValue); + builder.setReserveIncrement(reserveIncrementValue); + builder.setBaseFeeDrops(baseFeeDropsValue); + builder.setReserveBaseDrops(reserveBaseDropsValue); + builder.setReserveIncrementDrops(reserveIncrementDropsValue); + builder.setPreviousTxnID(previousTxnIDValue); + builder.setPreviousTxnLgrSeq(previousTxnLgrSeqValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = baseFeeValue; + auto const actualOpt = entry.getBaseFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfBaseFee"); + EXPECT_TRUE(entry.hasBaseFee()); + } + + { + auto const& expected = referenceFeeUnitsValue; + auto const actualOpt = entry.getReferenceFeeUnits(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfReferenceFeeUnits"); + EXPECT_TRUE(entry.hasReferenceFeeUnits()); + } + + { + auto const& expected = reserveBaseValue; + auto const actualOpt = entry.getReserveBase(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfReserveBase"); + EXPECT_TRUE(entry.hasReserveBase()); + } + + { + auto const& expected = reserveIncrementValue; + auto const actualOpt = entry.getReserveIncrement(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfReserveIncrement"); + EXPECT_TRUE(entry.hasReserveIncrement()); + } + + { + auto const& expected = baseFeeDropsValue; + auto const actualOpt = entry.getBaseFeeDrops(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfBaseFeeDrops"); + EXPECT_TRUE(entry.hasBaseFeeDrops()); + } + + { + auto const& expected = reserveBaseDropsValue; + auto const actualOpt = entry.getReserveBaseDrops(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfReserveBaseDrops"); + EXPECT_TRUE(entry.hasReserveBaseDrops()); + } + + { + auto const& expected = reserveIncrementDropsValue; + auto const actualOpt = entry.getReserveIncrementDrops(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfReserveIncrementDrops"); + EXPECT_TRUE(entry.hasReserveIncrementDrops()); + } + + { + auto const& expected = previousTxnIDValue; + auto const actualOpt = entry.getPreviousTxnID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnID"); + EXPECT_TRUE(entry.hasPreviousTxnID()); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actualOpt = entry.getPreviousTxnLgrSeq(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnLgrSeq"); + EXPECT_TRUE(entry.hasPreviousTxnLgrSeq()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(FeeSettingsTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const baseFeeValue = canonical_UINT64(); + auto const referenceFeeUnitsValue = canonical_UINT32(); + auto const reserveBaseValue = canonical_UINT32(); + auto const reserveIncrementValue = canonical_UINT32(); + auto const baseFeeDropsValue = canonical_AMOUNT(); + auto const reserveBaseDropsValue = canonical_AMOUNT(); + auto const reserveIncrementDropsValue = canonical_AMOUNT(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(FeeSettings::entryType, index); + + sle->at(sfBaseFee) = baseFeeValue; + sle->at(sfReferenceFeeUnits) = referenceFeeUnitsValue; + sle->at(sfReserveBase) = reserveBaseValue; + sle->at(sfReserveIncrement) = reserveIncrementValue; + sle->at(sfBaseFeeDrops) = baseFeeDropsValue; + sle->at(sfReserveBaseDrops) = reserveBaseDropsValue; + sle->at(sfReserveIncrementDrops) = reserveIncrementDropsValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + FeeSettingsBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + FeeSettings entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = baseFeeValue; + + auto const fromSleOpt = entryFromSle.getBaseFee(); + auto const fromBuilderOpt = entryFromBuilder.getBaseFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfBaseFee"); + expectEqualField(expected, *fromBuilderOpt, "sfBaseFee"); + } + + { + auto const& expected = referenceFeeUnitsValue; + + auto const fromSleOpt = entryFromSle.getReferenceFeeUnits(); + auto const fromBuilderOpt = entryFromBuilder.getReferenceFeeUnits(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfReferenceFeeUnits"); + expectEqualField(expected, *fromBuilderOpt, "sfReferenceFeeUnits"); + } + + { + auto const& expected = reserveBaseValue; + + auto const fromSleOpt = entryFromSle.getReserveBase(); + auto const fromBuilderOpt = entryFromBuilder.getReserveBase(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfReserveBase"); + expectEqualField(expected, *fromBuilderOpt, "sfReserveBase"); + } + + { + auto const& expected = reserveIncrementValue; + + auto const fromSleOpt = entryFromSle.getReserveIncrement(); + auto const fromBuilderOpt = entryFromBuilder.getReserveIncrement(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfReserveIncrement"); + expectEqualField(expected, *fromBuilderOpt, "sfReserveIncrement"); + } + + { + auto const& expected = baseFeeDropsValue; + + auto const fromSleOpt = entryFromSle.getBaseFeeDrops(); + auto const fromBuilderOpt = entryFromBuilder.getBaseFeeDrops(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfBaseFeeDrops"); + expectEqualField(expected, *fromBuilderOpt, "sfBaseFeeDrops"); + } + + { + auto const& expected = reserveBaseDropsValue; + + auto const fromSleOpt = entryFromSle.getReserveBaseDrops(); + auto const fromBuilderOpt = entryFromBuilder.getReserveBaseDrops(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfReserveBaseDrops"); + expectEqualField(expected, *fromBuilderOpt, "sfReserveBaseDrops"); + } + + { + auto const& expected = reserveIncrementDropsValue; + + auto const fromSleOpt = entryFromSle.getReserveIncrementDrops(); + auto const fromBuilderOpt = entryFromBuilder.getReserveIncrementDrops(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfReserveIncrementDrops"); + expectEqualField(expected, *fromBuilderOpt, "sfReserveIncrementDrops"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnID(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnID"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnLgrSeq(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(FeeSettingsTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(FeeSettings{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(FeeSettingsTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(FeeSettingsBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(FeeSettingsTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + + FeeSettingsBuilder builder{ + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasBaseFee()); + EXPECT_FALSE(entry.getBaseFee().has_value()); + EXPECT_FALSE(entry.hasReferenceFeeUnits()); + EXPECT_FALSE(entry.getReferenceFeeUnits().has_value()); + EXPECT_FALSE(entry.hasReserveBase()); + EXPECT_FALSE(entry.getReserveBase().has_value()); + EXPECT_FALSE(entry.hasReserveIncrement()); + EXPECT_FALSE(entry.getReserveIncrement().has_value()); + EXPECT_FALSE(entry.hasBaseFeeDrops()); + EXPECT_FALSE(entry.getBaseFeeDrops().has_value()); + EXPECT_FALSE(entry.hasReserveBaseDrops()); + EXPECT_FALSE(entry.getReserveBaseDrops().has_value()); + EXPECT_FALSE(entry.hasReserveIncrementDrops()); + EXPECT_FALSE(entry.getReserveIncrementDrops().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnID()); + EXPECT_FALSE(entry.getPreviousTxnID().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnLgrSeq()); + EXPECT_FALSE(entry.getPreviousTxnLgrSeq().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/LedgerHashesTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/LedgerHashesTests.cpp new file mode 100644 index 0000000000..8a7ae38749 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/LedgerHashesTests.cpp @@ -0,0 +1,192 @@ +// Auto-generated unit tests for ledger entry LedgerHashes + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(LedgerHashesTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const firstLedgerSequenceValue = canonical_UINT32(); + auto const lastLedgerSequenceValue = canonical_UINT32(); + auto const hashesValue = canonical_VECTOR256(); + + LedgerHashesBuilder builder{ + hashesValue + }; + + builder.setFirstLedgerSequence(firstLedgerSequenceValue); + builder.setLastLedgerSequence(lastLedgerSequenceValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = hashesValue; + auto const actual = entry.getHashes(); + expectEqualField(expected, actual, "sfHashes"); + } + + { + auto const& expected = firstLedgerSequenceValue; + auto const actualOpt = entry.getFirstLedgerSequence(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfFirstLedgerSequence"); + EXPECT_TRUE(entry.hasFirstLedgerSequence()); + } + + { + auto const& expected = lastLedgerSequenceValue; + auto const actualOpt = entry.getLastLedgerSequence(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLastLedgerSequence"); + EXPECT_TRUE(entry.hasLastLedgerSequence()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(LedgerHashesTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const firstLedgerSequenceValue = canonical_UINT32(); + auto const lastLedgerSequenceValue = canonical_UINT32(); + auto const hashesValue = canonical_VECTOR256(); + + auto sle = std::make_shared(LedgerHashes::entryType, index); + + sle->at(sfFirstLedgerSequence) = firstLedgerSequenceValue; + sle->at(sfLastLedgerSequence) = lastLedgerSequenceValue; + sle->at(sfHashes) = hashesValue; + + LedgerHashesBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + LedgerHashes entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = hashesValue; + + auto const fromSle = entryFromSle.getHashes(); + auto const fromBuilder = entryFromBuilder.getHashes(); + + expectEqualField(expected, fromSle, "sfHashes"); + expectEqualField(expected, fromBuilder, "sfHashes"); + } + + { + auto const& expected = firstLedgerSequenceValue; + + auto const fromSleOpt = entryFromSle.getFirstLedgerSequence(); + auto const fromBuilderOpt = entryFromBuilder.getFirstLedgerSequence(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfFirstLedgerSequence"); + expectEqualField(expected, *fromBuilderOpt, "sfFirstLedgerSequence"); + } + + { + auto const& expected = lastLedgerSequenceValue; + + auto const fromSleOpt = entryFromSle.getLastLedgerSequence(); + auto const fromBuilderOpt = entryFromBuilder.getLastLedgerSequence(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLastLedgerSequence"); + expectEqualField(expected, *fromBuilderOpt, "sfLastLedgerSequence"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(LedgerHashesTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(LedgerHashes{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(LedgerHashesTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(LedgerHashesBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(LedgerHashesTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const hashesValue = canonical_VECTOR256(); + + LedgerHashesBuilder builder{ + hashesValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasFirstLedgerSequence()); + EXPECT_FALSE(entry.getFirstLedgerSequence().has_value()); + EXPECT_FALSE(entry.hasLastLedgerSequence()); + EXPECT_FALSE(entry.getLastLedgerSequence().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanBrokerTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanBrokerTests.cpp new file mode 100644 index 0000000000..fba8c539d2 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanBrokerTests.cpp @@ -0,0 +1,530 @@ +// Auto-generated unit tests for ledger entry LoanBroker + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(LoanBrokerTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const vaultNodeValue = canonical_UINT64(); + auto const vaultIDValue = canonical_UINT256(); + auto const accountValue = canonical_ACCOUNT(); + auto const ownerValue = canonical_ACCOUNT(); + auto const loanSequenceValue = canonical_UINT32(); + auto const dataValue = canonical_VL(); + auto const managementFeeRateValue = canonical_UINT16(); + auto const ownerCountValue = canonical_UINT32(); + auto const debtTotalValue = canonical_NUMBER(); + auto const debtMaximumValue = canonical_NUMBER(); + auto const coverAvailableValue = canonical_NUMBER(); + auto const coverRateMinimumValue = canonical_UINT32(); + auto const coverRateLiquidationValue = canonical_UINT32(); + + LoanBrokerBuilder builder{ + previousTxnIDValue, + previousTxnLgrSeqValue, + sequenceValue, + ownerNodeValue, + vaultNodeValue, + vaultIDValue, + accountValue, + ownerValue, + loanSequenceValue + }; + + builder.setData(dataValue); + builder.setManagementFeeRate(managementFeeRateValue); + builder.setOwnerCount(ownerCountValue); + builder.setDebtTotal(debtTotalValue); + builder.setDebtMaximum(debtMaximumValue); + builder.setCoverAvailable(coverAvailableValue); + builder.setCoverRateMinimum(coverRateMinimumValue); + builder.setCoverRateLiquidation(coverRateLiquidationValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + auto const actual = entry.getSequence(); + expectEqualField(expected, actual, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = vaultNodeValue; + auto const actual = entry.getVaultNode(); + expectEqualField(expected, actual, "sfVaultNode"); + } + + { + auto const& expected = vaultIDValue; + auto const actual = entry.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = ownerValue; + auto const actual = entry.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = loanSequenceValue; + auto const actual = entry.getLoanSequence(); + expectEqualField(expected, actual, "sfLoanSequence"); + } + + { + auto const& expected = dataValue; + auto const actualOpt = entry.getData(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfData"); + EXPECT_TRUE(entry.hasData()); + } + + { + auto const& expected = managementFeeRateValue; + auto const actualOpt = entry.getManagementFeeRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfManagementFeeRate"); + EXPECT_TRUE(entry.hasManagementFeeRate()); + } + + { + auto const& expected = ownerCountValue; + auto const actualOpt = entry.getOwnerCount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfOwnerCount"); + EXPECT_TRUE(entry.hasOwnerCount()); + } + + { + auto const& expected = debtTotalValue; + auto const actualOpt = entry.getDebtTotal(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDebtTotal"); + EXPECT_TRUE(entry.hasDebtTotal()); + } + + { + auto const& expected = debtMaximumValue; + auto const actualOpt = entry.getDebtMaximum(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDebtMaximum"); + EXPECT_TRUE(entry.hasDebtMaximum()); + } + + { + auto const& expected = coverAvailableValue; + auto const actualOpt = entry.getCoverAvailable(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfCoverAvailable"); + EXPECT_TRUE(entry.hasCoverAvailable()); + } + + { + auto const& expected = coverRateMinimumValue; + auto const actualOpt = entry.getCoverRateMinimum(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfCoverRateMinimum"); + EXPECT_TRUE(entry.hasCoverRateMinimum()); + } + + { + auto const& expected = coverRateLiquidationValue; + auto const actualOpt = entry.getCoverRateLiquidation(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfCoverRateLiquidation"); + EXPECT_TRUE(entry.hasCoverRateLiquidation()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(LoanBrokerTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const vaultNodeValue = canonical_UINT64(); + auto const vaultIDValue = canonical_UINT256(); + auto const accountValue = canonical_ACCOUNT(); + auto const ownerValue = canonical_ACCOUNT(); + auto const loanSequenceValue = canonical_UINT32(); + auto const dataValue = canonical_VL(); + auto const managementFeeRateValue = canonical_UINT16(); + auto const ownerCountValue = canonical_UINT32(); + auto const debtTotalValue = canonical_NUMBER(); + auto const debtMaximumValue = canonical_NUMBER(); + auto const coverAvailableValue = canonical_NUMBER(); + auto const coverRateMinimumValue = canonical_UINT32(); + auto const coverRateLiquidationValue = canonical_UINT32(); + + auto sle = std::make_shared(LoanBroker::entryType, index); + + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfVaultNode) = vaultNodeValue; + sle->at(sfVaultID) = vaultIDValue; + sle->at(sfAccount) = accountValue; + sle->at(sfOwner) = ownerValue; + sle->at(sfLoanSequence) = loanSequenceValue; + sle->at(sfData) = dataValue; + sle->at(sfManagementFeeRate) = managementFeeRateValue; + sle->at(sfOwnerCount) = ownerCountValue; + sle->at(sfDebtTotal) = debtTotalValue; + sle->at(sfDebtMaximum) = debtMaximumValue; + sle->at(sfCoverAvailable) = coverAvailableValue; + sle->at(sfCoverRateMinimum) = coverRateMinimumValue; + sle->at(sfCoverRateLiquidation) = coverRateLiquidationValue; + + LoanBrokerBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + LoanBroker entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSle = entryFromSle.getSequence(); + auto const fromBuilder = entryFromBuilder.getSequence(); + + expectEqualField(expected, fromSle, "sfSequence"); + expectEqualField(expected, fromBuilder, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = vaultNodeValue; + + auto const fromSle = entryFromSle.getVaultNode(); + auto const fromBuilder = entryFromBuilder.getVaultNode(); + + expectEqualField(expected, fromSle, "sfVaultNode"); + expectEqualField(expected, fromBuilder, "sfVaultNode"); + } + + { + auto const& expected = vaultIDValue; + + auto const fromSle = entryFromSle.getVaultID(); + auto const fromBuilder = entryFromBuilder.getVaultID(); + + expectEqualField(expected, fromSle, "sfVaultID"); + expectEqualField(expected, fromBuilder, "sfVaultID"); + } + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = ownerValue; + + auto const fromSle = entryFromSle.getOwner(); + auto const fromBuilder = entryFromBuilder.getOwner(); + + expectEqualField(expected, fromSle, "sfOwner"); + expectEqualField(expected, fromBuilder, "sfOwner"); + } + + { + auto const& expected = loanSequenceValue; + + auto const fromSle = entryFromSle.getLoanSequence(); + auto const fromBuilder = entryFromBuilder.getLoanSequence(); + + expectEqualField(expected, fromSle, "sfLoanSequence"); + expectEqualField(expected, fromBuilder, "sfLoanSequence"); + } + + { + auto const& expected = dataValue; + + auto const fromSleOpt = entryFromSle.getData(); + auto const fromBuilderOpt = entryFromBuilder.getData(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfData"); + expectEqualField(expected, *fromBuilderOpt, "sfData"); + } + + { + auto const& expected = managementFeeRateValue; + + auto const fromSleOpt = entryFromSle.getManagementFeeRate(); + auto const fromBuilderOpt = entryFromBuilder.getManagementFeeRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfManagementFeeRate"); + expectEqualField(expected, *fromBuilderOpt, "sfManagementFeeRate"); + } + + { + auto const& expected = ownerCountValue; + + auto const fromSleOpt = entryFromSle.getOwnerCount(); + auto const fromBuilderOpt = entryFromBuilder.getOwnerCount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfOwnerCount"); + expectEqualField(expected, *fromBuilderOpt, "sfOwnerCount"); + } + + { + auto const& expected = debtTotalValue; + + auto const fromSleOpt = entryFromSle.getDebtTotal(); + auto const fromBuilderOpt = entryFromBuilder.getDebtTotal(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDebtTotal"); + expectEqualField(expected, *fromBuilderOpt, "sfDebtTotal"); + } + + { + auto const& expected = debtMaximumValue; + + auto const fromSleOpt = entryFromSle.getDebtMaximum(); + auto const fromBuilderOpt = entryFromBuilder.getDebtMaximum(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDebtMaximum"); + expectEqualField(expected, *fromBuilderOpt, "sfDebtMaximum"); + } + + { + auto const& expected = coverAvailableValue; + + auto const fromSleOpt = entryFromSle.getCoverAvailable(); + auto const fromBuilderOpt = entryFromBuilder.getCoverAvailable(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfCoverAvailable"); + expectEqualField(expected, *fromBuilderOpt, "sfCoverAvailable"); + } + + { + auto const& expected = coverRateMinimumValue; + + auto const fromSleOpt = entryFromSle.getCoverRateMinimum(); + auto const fromBuilderOpt = entryFromBuilder.getCoverRateMinimum(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfCoverRateMinimum"); + expectEqualField(expected, *fromBuilderOpt, "sfCoverRateMinimum"); + } + + { + auto const& expected = coverRateLiquidationValue; + + auto const fromSleOpt = entryFromSle.getCoverRateLiquidation(); + auto const fromBuilderOpt = entryFromBuilder.getCoverRateLiquidation(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfCoverRateLiquidation"); + expectEqualField(expected, *fromBuilderOpt, "sfCoverRateLiquidation"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(LoanBrokerTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(LoanBroker{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(LoanBrokerTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(LoanBrokerBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(LoanBrokerTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const vaultNodeValue = canonical_UINT64(); + auto const vaultIDValue = canonical_UINT256(); + auto const accountValue = canonical_ACCOUNT(); + auto const ownerValue = canonical_ACCOUNT(); + auto const loanSequenceValue = canonical_UINT32(); + + LoanBrokerBuilder builder{ + previousTxnIDValue, + previousTxnLgrSeqValue, + sequenceValue, + ownerNodeValue, + vaultNodeValue, + vaultIDValue, + accountValue, + ownerValue, + loanSequenceValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasData()); + EXPECT_FALSE(entry.getData().has_value()); + EXPECT_FALSE(entry.hasManagementFeeRate()); + EXPECT_FALSE(entry.getManagementFeeRate().has_value()); + EXPECT_FALSE(entry.hasOwnerCount()); + EXPECT_FALSE(entry.getOwnerCount().has_value()); + EXPECT_FALSE(entry.hasDebtTotal()); + EXPECT_FALSE(entry.getDebtTotal().has_value()); + EXPECT_FALSE(entry.hasDebtMaximum()); + EXPECT_FALSE(entry.getDebtMaximum().has_value()); + EXPECT_FALSE(entry.hasCoverAvailable()); + EXPECT_FALSE(entry.getCoverAvailable().has_value()); + EXPECT_FALSE(entry.hasCoverRateMinimum()); + EXPECT_FALSE(entry.getCoverRateMinimum().has_value()); + EXPECT_FALSE(entry.hasCoverRateLiquidation()); + EXPECT_FALSE(entry.getCoverRateLiquidation().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanTests.cpp new file mode 100644 index 0000000000..845a8337b9 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanTests.cpp @@ -0,0 +1,795 @@ +// Auto-generated unit tests for ledger entry Loan + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(LoanTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const loanBrokerNodeValue = canonical_UINT64(); + auto const loanBrokerIDValue = canonical_UINT256(); + auto const loanSequenceValue = canonical_UINT32(); + auto const borrowerValue = canonical_ACCOUNT(); + auto const loanOriginationFeeValue = canonical_NUMBER(); + auto const loanServiceFeeValue = canonical_NUMBER(); + auto const latePaymentFeeValue = canonical_NUMBER(); + auto const closePaymentFeeValue = canonical_NUMBER(); + auto const overpaymentFeeValue = canonical_UINT32(); + auto const interestRateValue = canonical_UINT32(); + auto const lateInterestRateValue = canonical_UINT32(); + auto const closeInterestRateValue = canonical_UINT32(); + auto const overpaymentInterestRateValue = canonical_UINT32(); + auto const startDateValue = canonical_UINT32(); + auto const paymentIntervalValue = canonical_UINT32(); + auto const gracePeriodValue = canonical_UINT32(); + auto const previousPaymentDueDateValue = canonical_UINT32(); + auto const nextPaymentDueDateValue = canonical_UINT32(); + auto const paymentRemainingValue = canonical_UINT32(); + auto const periodicPaymentValue = canonical_NUMBER(); + auto const principalOutstandingValue = canonical_NUMBER(); + auto const totalValueOutstandingValue = canonical_NUMBER(); + auto const managementFeeOutstandingValue = canonical_NUMBER(); + auto const loanScaleValue = canonical_INT32(); + + LoanBuilder builder{ + previousTxnIDValue, + previousTxnLgrSeqValue, + ownerNodeValue, + loanBrokerNodeValue, + loanBrokerIDValue, + loanSequenceValue, + borrowerValue, + startDateValue, + paymentIntervalValue, + periodicPaymentValue + }; + + builder.setLoanOriginationFee(loanOriginationFeeValue); + builder.setLoanServiceFee(loanServiceFeeValue); + builder.setLatePaymentFee(latePaymentFeeValue); + builder.setClosePaymentFee(closePaymentFeeValue); + builder.setOverpaymentFee(overpaymentFeeValue); + builder.setInterestRate(interestRateValue); + builder.setLateInterestRate(lateInterestRateValue); + builder.setCloseInterestRate(closeInterestRateValue); + builder.setOverpaymentInterestRate(overpaymentInterestRateValue); + builder.setGracePeriod(gracePeriodValue); + builder.setPreviousPaymentDueDate(previousPaymentDueDateValue); + builder.setNextPaymentDueDate(nextPaymentDueDateValue); + builder.setPaymentRemaining(paymentRemainingValue); + builder.setPrincipalOutstanding(principalOutstandingValue); + builder.setTotalValueOutstanding(totalValueOutstandingValue); + builder.setManagementFeeOutstanding(managementFeeOutstandingValue); + builder.setLoanScale(loanScaleValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = loanBrokerNodeValue; + auto const actual = entry.getLoanBrokerNode(); + expectEqualField(expected, actual, "sfLoanBrokerNode"); + } + + { + auto const& expected = loanBrokerIDValue; + auto const actual = entry.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + { + auto const& expected = loanSequenceValue; + auto const actual = entry.getLoanSequence(); + expectEqualField(expected, actual, "sfLoanSequence"); + } + + { + auto const& expected = borrowerValue; + auto const actual = entry.getBorrower(); + expectEqualField(expected, actual, "sfBorrower"); + } + + { + auto const& expected = startDateValue; + auto const actual = entry.getStartDate(); + expectEqualField(expected, actual, "sfStartDate"); + } + + { + auto const& expected = paymentIntervalValue; + auto const actual = entry.getPaymentInterval(); + expectEqualField(expected, actual, "sfPaymentInterval"); + } + + { + auto const& expected = periodicPaymentValue; + auto const actual = entry.getPeriodicPayment(); + expectEqualField(expected, actual, "sfPeriodicPayment"); + } + + { + auto const& expected = loanOriginationFeeValue; + auto const actualOpt = entry.getLoanOriginationFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLoanOriginationFee"); + EXPECT_TRUE(entry.hasLoanOriginationFee()); + } + + { + auto const& expected = loanServiceFeeValue; + auto const actualOpt = entry.getLoanServiceFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLoanServiceFee"); + EXPECT_TRUE(entry.hasLoanServiceFee()); + } + + { + auto const& expected = latePaymentFeeValue; + auto const actualOpt = entry.getLatePaymentFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLatePaymentFee"); + EXPECT_TRUE(entry.hasLatePaymentFee()); + } + + { + auto const& expected = closePaymentFeeValue; + auto const actualOpt = entry.getClosePaymentFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfClosePaymentFee"); + EXPECT_TRUE(entry.hasClosePaymentFee()); + } + + { + auto const& expected = overpaymentFeeValue; + auto const actualOpt = entry.getOverpaymentFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfOverpaymentFee"); + EXPECT_TRUE(entry.hasOverpaymentFee()); + } + + { + auto const& expected = interestRateValue; + auto const actualOpt = entry.getInterestRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfInterestRate"); + EXPECT_TRUE(entry.hasInterestRate()); + } + + { + auto const& expected = lateInterestRateValue; + auto const actualOpt = entry.getLateInterestRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLateInterestRate"); + EXPECT_TRUE(entry.hasLateInterestRate()); + } + + { + auto const& expected = closeInterestRateValue; + auto const actualOpt = entry.getCloseInterestRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfCloseInterestRate"); + EXPECT_TRUE(entry.hasCloseInterestRate()); + } + + { + auto const& expected = overpaymentInterestRateValue; + auto const actualOpt = entry.getOverpaymentInterestRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfOverpaymentInterestRate"); + EXPECT_TRUE(entry.hasOverpaymentInterestRate()); + } + + { + auto const& expected = gracePeriodValue; + auto const actualOpt = entry.getGracePeriod(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfGracePeriod"); + EXPECT_TRUE(entry.hasGracePeriod()); + } + + { + auto const& expected = previousPaymentDueDateValue; + auto const actualOpt = entry.getPreviousPaymentDueDate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousPaymentDueDate"); + EXPECT_TRUE(entry.hasPreviousPaymentDueDate()); + } + + { + auto const& expected = nextPaymentDueDateValue; + auto const actualOpt = entry.getNextPaymentDueDate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfNextPaymentDueDate"); + EXPECT_TRUE(entry.hasNextPaymentDueDate()); + } + + { + auto const& expected = paymentRemainingValue; + auto const actualOpt = entry.getPaymentRemaining(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPaymentRemaining"); + EXPECT_TRUE(entry.hasPaymentRemaining()); + } + + { + auto const& expected = principalOutstandingValue; + auto const actualOpt = entry.getPrincipalOutstanding(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPrincipalOutstanding"); + EXPECT_TRUE(entry.hasPrincipalOutstanding()); + } + + { + auto const& expected = totalValueOutstandingValue; + auto const actualOpt = entry.getTotalValueOutstanding(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTotalValueOutstanding"); + EXPECT_TRUE(entry.hasTotalValueOutstanding()); + } + + { + auto const& expected = managementFeeOutstandingValue; + auto const actualOpt = entry.getManagementFeeOutstanding(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfManagementFeeOutstanding"); + EXPECT_TRUE(entry.hasManagementFeeOutstanding()); + } + + { + auto const& expected = loanScaleValue; + auto const actualOpt = entry.getLoanScale(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLoanScale"); + EXPECT_TRUE(entry.hasLoanScale()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(LoanTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const loanBrokerNodeValue = canonical_UINT64(); + auto const loanBrokerIDValue = canonical_UINT256(); + auto const loanSequenceValue = canonical_UINT32(); + auto const borrowerValue = canonical_ACCOUNT(); + auto const loanOriginationFeeValue = canonical_NUMBER(); + auto const loanServiceFeeValue = canonical_NUMBER(); + auto const latePaymentFeeValue = canonical_NUMBER(); + auto const closePaymentFeeValue = canonical_NUMBER(); + auto const overpaymentFeeValue = canonical_UINT32(); + auto const interestRateValue = canonical_UINT32(); + auto const lateInterestRateValue = canonical_UINT32(); + auto const closeInterestRateValue = canonical_UINT32(); + auto const overpaymentInterestRateValue = canonical_UINT32(); + auto const startDateValue = canonical_UINT32(); + auto const paymentIntervalValue = canonical_UINT32(); + auto const gracePeriodValue = canonical_UINT32(); + auto const previousPaymentDueDateValue = canonical_UINT32(); + auto const nextPaymentDueDateValue = canonical_UINT32(); + auto const paymentRemainingValue = canonical_UINT32(); + auto const periodicPaymentValue = canonical_NUMBER(); + auto const principalOutstandingValue = canonical_NUMBER(); + auto const totalValueOutstandingValue = canonical_NUMBER(); + auto const managementFeeOutstandingValue = canonical_NUMBER(); + auto const loanScaleValue = canonical_INT32(); + + auto sle = std::make_shared(Loan::entryType, index); + + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfLoanBrokerNode) = loanBrokerNodeValue; + sle->at(sfLoanBrokerID) = loanBrokerIDValue; + sle->at(sfLoanSequence) = loanSequenceValue; + sle->at(sfBorrower) = borrowerValue; + sle->at(sfLoanOriginationFee) = loanOriginationFeeValue; + sle->at(sfLoanServiceFee) = loanServiceFeeValue; + sle->at(sfLatePaymentFee) = latePaymentFeeValue; + sle->at(sfClosePaymentFee) = closePaymentFeeValue; + sle->at(sfOverpaymentFee) = overpaymentFeeValue; + sle->at(sfInterestRate) = interestRateValue; + sle->at(sfLateInterestRate) = lateInterestRateValue; + sle->at(sfCloseInterestRate) = closeInterestRateValue; + sle->at(sfOverpaymentInterestRate) = overpaymentInterestRateValue; + sle->at(sfStartDate) = startDateValue; + sle->at(sfPaymentInterval) = paymentIntervalValue; + sle->at(sfGracePeriod) = gracePeriodValue; + sle->at(sfPreviousPaymentDueDate) = previousPaymentDueDateValue; + sle->at(sfNextPaymentDueDate) = nextPaymentDueDateValue; + sle->at(sfPaymentRemaining) = paymentRemainingValue; + sle->at(sfPeriodicPayment) = periodicPaymentValue; + sle->at(sfPrincipalOutstanding) = principalOutstandingValue; + sle->at(sfTotalValueOutstanding) = totalValueOutstandingValue; + sle->at(sfManagementFeeOutstanding) = managementFeeOutstandingValue; + sle->at(sfLoanScale) = loanScaleValue; + + LoanBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Loan entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = loanBrokerNodeValue; + + auto const fromSle = entryFromSle.getLoanBrokerNode(); + auto const fromBuilder = entryFromBuilder.getLoanBrokerNode(); + + expectEqualField(expected, fromSle, "sfLoanBrokerNode"); + expectEqualField(expected, fromBuilder, "sfLoanBrokerNode"); + } + + { + auto const& expected = loanBrokerIDValue; + + auto const fromSle = entryFromSle.getLoanBrokerID(); + auto const fromBuilder = entryFromBuilder.getLoanBrokerID(); + + expectEqualField(expected, fromSle, "sfLoanBrokerID"); + expectEqualField(expected, fromBuilder, "sfLoanBrokerID"); + } + + { + auto const& expected = loanSequenceValue; + + auto const fromSle = entryFromSle.getLoanSequence(); + auto const fromBuilder = entryFromBuilder.getLoanSequence(); + + expectEqualField(expected, fromSle, "sfLoanSequence"); + expectEqualField(expected, fromBuilder, "sfLoanSequence"); + } + + { + auto const& expected = borrowerValue; + + auto const fromSle = entryFromSle.getBorrower(); + auto const fromBuilder = entryFromBuilder.getBorrower(); + + expectEqualField(expected, fromSle, "sfBorrower"); + expectEqualField(expected, fromBuilder, "sfBorrower"); + } + + { + auto const& expected = startDateValue; + + auto const fromSle = entryFromSle.getStartDate(); + auto const fromBuilder = entryFromBuilder.getStartDate(); + + expectEqualField(expected, fromSle, "sfStartDate"); + expectEqualField(expected, fromBuilder, "sfStartDate"); + } + + { + auto const& expected = paymentIntervalValue; + + auto const fromSle = entryFromSle.getPaymentInterval(); + auto const fromBuilder = entryFromBuilder.getPaymentInterval(); + + expectEqualField(expected, fromSle, "sfPaymentInterval"); + expectEqualField(expected, fromBuilder, "sfPaymentInterval"); + } + + { + auto const& expected = periodicPaymentValue; + + auto const fromSle = entryFromSle.getPeriodicPayment(); + auto const fromBuilder = entryFromBuilder.getPeriodicPayment(); + + expectEqualField(expected, fromSle, "sfPeriodicPayment"); + expectEqualField(expected, fromBuilder, "sfPeriodicPayment"); + } + + { + auto const& expected = loanOriginationFeeValue; + + auto const fromSleOpt = entryFromSle.getLoanOriginationFee(); + auto const fromBuilderOpt = entryFromBuilder.getLoanOriginationFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLoanOriginationFee"); + expectEqualField(expected, *fromBuilderOpt, "sfLoanOriginationFee"); + } + + { + auto const& expected = loanServiceFeeValue; + + auto const fromSleOpt = entryFromSle.getLoanServiceFee(); + auto const fromBuilderOpt = entryFromBuilder.getLoanServiceFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLoanServiceFee"); + expectEqualField(expected, *fromBuilderOpt, "sfLoanServiceFee"); + } + + { + auto const& expected = latePaymentFeeValue; + + auto const fromSleOpt = entryFromSle.getLatePaymentFee(); + auto const fromBuilderOpt = entryFromBuilder.getLatePaymentFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLatePaymentFee"); + expectEqualField(expected, *fromBuilderOpt, "sfLatePaymentFee"); + } + + { + auto const& expected = closePaymentFeeValue; + + auto const fromSleOpt = entryFromSle.getClosePaymentFee(); + auto const fromBuilderOpt = entryFromBuilder.getClosePaymentFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfClosePaymentFee"); + expectEqualField(expected, *fromBuilderOpt, "sfClosePaymentFee"); + } + + { + auto const& expected = overpaymentFeeValue; + + auto const fromSleOpt = entryFromSle.getOverpaymentFee(); + auto const fromBuilderOpt = entryFromBuilder.getOverpaymentFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfOverpaymentFee"); + expectEqualField(expected, *fromBuilderOpt, "sfOverpaymentFee"); + } + + { + auto const& expected = interestRateValue; + + auto const fromSleOpt = entryFromSle.getInterestRate(); + auto const fromBuilderOpt = entryFromBuilder.getInterestRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfInterestRate"); + expectEqualField(expected, *fromBuilderOpt, "sfInterestRate"); + } + + { + auto const& expected = lateInterestRateValue; + + auto const fromSleOpt = entryFromSle.getLateInterestRate(); + auto const fromBuilderOpt = entryFromBuilder.getLateInterestRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLateInterestRate"); + expectEqualField(expected, *fromBuilderOpt, "sfLateInterestRate"); + } + + { + auto const& expected = closeInterestRateValue; + + auto const fromSleOpt = entryFromSle.getCloseInterestRate(); + auto const fromBuilderOpt = entryFromBuilder.getCloseInterestRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfCloseInterestRate"); + expectEqualField(expected, *fromBuilderOpt, "sfCloseInterestRate"); + } + + { + auto const& expected = overpaymentInterestRateValue; + + auto const fromSleOpt = entryFromSle.getOverpaymentInterestRate(); + auto const fromBuilderOpt = entryFromBuilder.getOverpaymentInterestRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfOverpaymentInterestRate"); + expectEqualField(expected, *fromBuilderOpt, "sfOverpaymentInterestRate"); + } + + { + auto const& expected = gracePeriodValue; + + auto const fromSleOpt = entryFromSle.getGracePeriod(); + auto const fromBuilderOpt = entryFromBuilder.getGracePeriod(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfGracePeriod"); + expectEqualField(expected, *fromBuilderOpt, "sfGracePeriod"); + } + + { + auto const& expected = previousPaymentDueDateValue; + + auto const fromSleOpt = entryFromSle.getPreviousPaymentDueDate(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousPaymentDueDate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousPaymentDueDate"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousPaymentDueDate"); + } + + { + auto const& expected = nextPaymentDueDateValue; + + auto const fromSleOpt = entryFromSle.getNextPaymentDueDate(); + auto const fromBuilderOpt = entryFromBuilder.getNextPaymentDueDate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfNextPaymentDueDate"); + expectEqualField(expected, *fromBuilderOpt, "sfNextPaymentDueDate"); + } + + { + auto const& expected = paymentRemainingValue; + + auto const fromSleOpt = entryFromSle.getPaymentRemaining(); + auto const fromBuilderOpt = entryFromBuilder.getPaymentRemaining(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPaymentRemaining"); + expectEqualField(expected, *fromBuilderOpt, "sfPaymentRemaining"); + } + + { + auto const& expected = principalOutstandingValue; + + auto const fromSleOpt = entryFromSle.getPrincipalOutstanding(); + auto const fromBuilderOpt = entryFromBuilder.getPrincipalOutstanding(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPrincipalOutstanding"); + expectEqualField(expected, *fromBuilderOpt, "sfPrincipalOutstanding"); + } + + { + auto const& expected = totalValueOutstandingValue; + + auto const fromSleOpt = entryFromSle.getTotalValueOutstanding(); + auto const fromBuilderOpt = entryFromBuilder.getTotalValueOutstanding(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTotalValueOutstanding"); + expectEqualField(expected, *fromBuilderOpt, "sfTotalValueOutstanding"); + } + + { + auto const& expected = managementFeeOutstandingValue; + + auto const fromSleOpt = entryFromSle.getManagementFeeOutstanding(); + auto const fromBuilderOpt = entryFromBuilder.getManagementFeeOutstanding(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfManagementFeeOutstanding"); + expectEqualField(expected, *fromBuilderOpt, "sfManagementFeeOutstanding"); + } + + { + auto const& expected = loanScaleValue; + + auto const fromSleOpt = entryFromSle.getLoanScale(); + auto const fromBuilderOpt = entryFromBuilder.getLoanScale(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLoanScale"); + expectEqualField(expected, *fromBuilderOpt, "sfLoanScale"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(LoanTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Loan{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(LoanTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(LoanBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(LoanTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const loanBrokerNodeValue = canonical_UINT64(); + auto const loanBrokerIDValue = canonical_UINT256(); + auto const loanSequenceValue = canonical_UINT32(); + auto const borrowerValue = canonical_ACCOUNT(); + auto const startDateValue = canonical_UINT32(); + auto const paymentIntervalValue = canonical_UINT32(); + auto const periodicPaymentValue = canonical_NUMBER(); + + LoanBuilder builder{ + previousTxnIDValue, + previousTxnLgrSeqValue, + ownerNodeValue, + loanBrokerNodeValue, + loanBrokerIDValue, + loanSequenceValue, + borrowerValue, + startDateValue, + paymentIntervalValue, + periodicPaymentValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasLoanOriginationFee()); + EXPECT_FALSE(entry.getLoanOriginationFee().has_value()); + EXPECT_FALSE(entry.hasLoanServiceFee()); + EXPECT_FALSE(entry.getLoanServiceFee().has_value()); + EXPECT_FALSE(entry.hasLatePaymentFee()); + EXPECT_FALSE(entry.getLatePaymentFee().has_value()); + EXPECT_FALSE(entry.hasClosePaymentFee()); + EXPECT_FALSE(entry.getClosePaymentFee().has_value()); + EXPECT_FALSE(entry.hasOverpaymentFee()); + EXPECT_FALSE(entry.getOverpaymentFee().has_value()); + EXPECT_FALSE(entry.hasInterestRate()); + EXPECT_FALSE(entry.getInterestRate().has_value()); + EXPECT_FALSE(entry.hasLateInterestRate()); + EXPECT_FALSE(entry.getLateInterestRate().has_value()); + EXPECT_FALSE(entry.hasCloseInterestRate()); + EXPECT_FALSE(entry.getCloseInterestRate().has_value()); + EXPECT_FALSE(entry.hasOverpaymentInterestRate()); + EXPECT_FALSE(entry.getOverpaymentInterestRate().has_value()); + EXPECT_FALSE(entry.hasGracePeriod()); + EXPECT_FALSE(entry.getGracePeriod().has_value()); + EXPECT_FALSE(entry.hasPreviousPaymentDueDate()); + EXPECT_FALSE(entry.getPreviousPaymentDueDate().has_value()); + EXPECT_FALSE(entry.hasNextPaymentDueDate()); + EXPECT_FALSE(entry.getNextPaymentDueDate().has_value()); + EXPECT_FALSE(entry.hasPaymentRemaining()); + EXPECT_FALSE(entry.getPaymentRemaining().has_value()); + EXPECT_FALSE(entry.hasPrincipalOutstanding()); + EXPECT_FALSE(entry.getPrincipalOutstanding().has_value()); + EXPECT_FALSE(entry.hasTotalValueOutstanding()); + EXPECT_FALSE(entry.getTotalValueOutstanding().has_value()); + EXPECT_FALSE(entry.hasManagementFeeOutstanding()); + EXPECT_FALSE(entry.getManagementFeeOutstanding().has_value()); + EXPECT_FALSE(entry.hasLoanScale()); + EXPECT_FALSE(entry.getLoanScale().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp new file mode 100644 index 0000000000..e74af94a5f --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp @@ -0,0 +1,437 @@ +// Auto-generated unit tests for ledger entry MPTokenIssuance + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(MPTokenIssuanceTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const issuerValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const transferFeeValue = canonical_UINT16(); + auto const ownerNodeValue = canonical_UINT64(); + auto const assetScaleValue = canonical_UINT8(); + auto const maximumAmountValue = canonical_UINT64(); + auto const outstandingAmountValue = canonical_UINT64(); + auto const lockedAmountValue = canonical_UINT64(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + auto const mutableFlagsValue = canonical_UINT32(); + + MPTokenIssuanceBuilder builder{ + issuerValue, + sequenceValue, + ownerNodeValue, + outstandingAmountValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setTransferFee(transferFeeValue); + builder.setAssetScale(assetScaleValue); + builder.setMaximumAmount(maximumAmountValue); + builder.setLockedAmount(lockedAmountValue); + builder.setMPTokenMetadata(mPTokenMetadataValue); + builder.setDomainID(domainIDValue); + builder.setMutableFlags(mutableFlagsValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = issuerValue; + auto const actual = entry.getIssuer(); + expectEqualField(expected, actual, "sfIssuer"); + } + + { + auto const& expected = sequenceValue; + auto const actual = entry.getSequence(); + expectEqualField(expected, actual, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = outstandingAmountValue; + auto const actual = entry.getOutstandingAmount(); + expectEqualField(expected, actual, "sfOutstandingAmount"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = transferFeeValue; + auto const actualOpt = entry.getTransferFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTransferFee"); + EXPECT_TRUE(entry.hasTransferFee()); + } + + { + auto const& expected = assetScaleValue; + auto const actualOpt = entry.getAssetScale(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAssetScale"); + EXPECT_TRUE(entry.hasAssetScale()); + } + + { + auto const& expected = maximumAmountValue; + auto const actualOpt = entry.getMaximumAmount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMaximumAmount"); + EXPECT_TRUE(entry.hasMaximumAmount()); + } + + { + auto const& expected = lockedAmountValue; + auto const actualOpt = entry.getLockedAmount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLockedAmount"); + EXPECT_TRUE(entry.hasLockedAmount()); + } + + { + auto const& expected = mPTokenMetadataValue; + auto const actualOpt = entry.getMPTokenMetadata(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMPTokenMetadata"); + EXPECT_TRUE(entry.hasMPTokenMetadata()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = entry.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(entry.hasDomainID()); + } + + { + auto const& expected = mutableFlagsValue; + auto const actualOpt = entry.getMutableFlags(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMutableFlags"); + EXPECT_TRUE(entry.hasMutableFlags()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(MPTokenIssuanceTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const issuerValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const transferFeeValue = canonical_UINT16(); + auto const ownerNodeValue = canonical_UINT64(); + auto const assetScaleValue = canonical_UINT8(); + auto const maximumAmountValue = canonical_UINT64(); + auto const outstandingAmountValue = canonical_UINT64(); + auto const lockedAmountValue = canonical_UINT64(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + auto const mutableFlagsValue = canonical_UINT32(); + + auto sle = std::make_shared(MPTokenIssuance::entryType, index); + + sle->at(sfIssuer) = issuerValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfTransferFee) = transferFeeValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfAssetScale) = assetScaleValue; + sle->at(sfMaximumAmount) = maximumAmountValue; + sle->at(sfOutstandingAmount) = outstandingAmountValue; + sle->at(sfLockedAmount) = lockedAmountValue; + sle->at(sfMPTokenMetadata) = mPTokenMetadataValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfDomainID) = domainIDValue; + sle->at(sfMutableFlags) = mutableFlagsValue; + + MPTokenIssuanceBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + MPTokenIssuance entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = issuerValue; + + auto const fromSle = entryFromSle.getIssuer(); + auto const fromBuilder = entryFromBuilder.getIssuer(); + + expectEqualField(expected, fromSle, "sfIssuer"); + expectEqualField(expected, fromBuilder, "sfIssuer"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSle = entryFromSle.getSequence(); + auto const fromBuilder = entryFromBuilder.getSequence(); + + expectEqualField(expected, fromSle, "sfSequence"); + expectEqualField(expected, fromBuilder, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = outstandingAmountValue; + + auto const fromSle = entryFromSle.getOutstandingAmount(); + auto const fromBuilder = entryFromBuilder.getOutstandingAmount(); + + expectEqualField(expected, fromSle, "sfOutstandingAmount"); + expectEqualField(expected, fromBuilder, "sfOutstandingAmount"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = transferFeeValue; + + auto const fromSleOpt = entryFromSle.getTransferFee(); + auto const fromBuilderOpt = entryFromBuilder.getTransferFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTransferFee"); + expectEqualField(expected, *fromBuilderOpt, "sfTransferFee"); + } + + { + auto const& expected = assetScaleValue; + + auto const fromSleOpt = entryFromSle.getAssetScale(); + auto const fromBuilderOpt = entryFromBuilder.getAssetScale(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAssetScale"); + expectEqualField(expected, *fromBuilderOpt, "sfAssetScale"); + } + + { + auto const& expected = maximumAmountValue; + + auto const fromSleOpt = entryFromSle.getMaximumAmount(); + auto const fromBuilderOpt = entryFromBuilder.getMaximumAmount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMaximumAmount"); + expectEqualField(expected, *fromBuilderOpt, "sfMaximumAmount"); + } + + { + auto const& expected = lockedAmountValue; + + auto const fromSleOpt = entryFromSle.getLockedAmount(); + auto const fromBuilderOpt = entryFromBuilder.getLockedAmount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLockedAmount"); + expectEqualField(expected, *fromBuilderOpt, "sfLockedAmount"); + } + + { + auto const& expected = mPTokenMetadataValue; + + auto const fromSleOpt = entryFromSle.getMPTokenMetadata(); + auto const fromBuilderOpt = entryFromBuilder.getMPTokenMetadata(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMPTokenMetadata"); + expectEqualField(expected, *fromBuilderOpt, "sfMPTokenMetadata"); + } + + { + auto const& expected = domainIDValue; + + auto const fromSleOpt = entryFromSle.getDomainID(); + auto const fromBuilderOpt = entryFromBuilder.getDomainID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDomainID"); + expectEqualField(expected, *fromBuilderOpt, "sfDomainID"); + } + + { + auto const& expected = mutableFlagsValue; + + auto const fromSleOpt = entryFromSle.getMutableFlags(); + auto const fromBuilderOpt = entryFromBuilder.getMutableFlags(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMutableFlags"); + expectEqualField(expected, *fromBuilderOpt, "sfMutableFlags"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(MPTokenIssuanceTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(MPTokenIssuance{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(MPTokenIssuanceTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(MPTokenIssuanceBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(MPTokenIssuanceTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const issuerValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const outstandingAmountValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + MPTokenIssuanceBuilder builder{ + issuerValue, + sequenceValue, + ownerNodeValue, + outstandingAmountValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasTransferFee()); + EXPECT_FALSE(entry.getTransferFee().has_value()); + EXPECT_FALSE(entry.hasAssetScale()); + EXPECT_FALSE(entry.getAssetScale().has_value()); + EXPECT_FALSE(entry.hasMaximumAmount()); + EXPECT_FALSE(entry.getMaximumAmount().has_value()); + EXPECT_FALSE(entry.hasLockedAmount()); + EXPECT_FALSE(entry.getLockedAmount().has_value()); + EXPECT_FALSE(entry.hasMPTokenMetadata()); + EXPECT_FALSE(entry.getMPTokenMetadata().has_value()); + EXPECT_FALSE(entry.hasDomainID()); + EXPECT_FALSE(entry.getDomainID().has_value()); + EXPECT_FALSE(entry.hasMutableFlags()); + EXPECT_FALSE(entry.getMutableFlags().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenTests.cpp new file mode 100644 index 0000000000..c104e7b365 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenTests.cpp @@ -0,0 +1,280 @@ +// Auto-generated unit tests for ledger entry MPToken + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(MPTokenTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const mPTAmountValue = canonical_UINT64(); + auto const lockedAmountValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + MPTokenBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setMPTAmount(mPTAmountValue); + builder.setLockedAmount(lockedAmountValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = entry.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = mPTAmountValue; + auto const actualOpt = entry.getMPTAmount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMPTAmount"); + EXPECT_TRUE(entry.hasMPTAmount()); + } + + { + auto const& expected = lockedAmountValue; + auto const actualOpt = entry.getLockedAmount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLockedAmount"); + EXPECT_TRUE(entry.hasLockedAmount()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(MPTokenTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const mPTAmountValue = canonical_UINT64(); + auto const lockedAmountValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(MPToken::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfMPTokenIssuanceID) = mPTokenIssuanceIDValue; + sle->at(sfMPTAmount) = mPTAmountValue; + sle->at(sfLockedAmount) = lockedAmountValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + MPTokenBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + MPToken entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = mPTokenIssuanceIDValue; + + auto const fromSle = entryFromSle.getMPTokenIssuanceID(); + auto const fromBuilder = entryFromBuilder.getMPTokenIssuanceID(); + + expectEqualField(expected, fromSle, "sfMPTokenIssuanceID"); + expectEqualField(expected, fromBuilder, "sfMPTokenIssuanceID"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = mPTAmountValue; + + auto const fromSleOpt = entryFromSle.getMPTAmount(); + auto const fromBuilderOpt = entryFromBuilder.getMPTAmount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMPTAmount"); + expectEqualField(expected, *fromBuilderOpt, "sfMPTAmount"); + } + + { + auto const& expected = lockedAmountValue; + + auto const fromSleOpt = entryFromSle.getLockedAmount(); + auto const fromBuilderOpt = entryFromBuilder.getLockedAmount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLockedAmount"); + expectEqualField(expected, *fromBuilderOpt, "sfLockedAmount"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(MPTokenTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(MPToken{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(MPTokenTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(MPTokenBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(MPTokenTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + MPTokenBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasMPTAmount()); + EXPECT_FALSE(entry.getMPTAmount().has_value()); + EXPECT_FALSE(entry.hasLockedAmount()); + EXPECT_FALSE(entry.getLockedAmount().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenOfferTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenOfferTests.cpp new file mode 100644 index 0000000000..9d039b2c38 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenOfferTests.cpp @@ -0,0 +1,324 @@ +// Auto-generated unit tests for ledger entry NFTokenOffer + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(NFTokenOfferTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const nFTokenIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const nFTokenOfferNodeValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + auto const expirationValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + NFTokenOfferBuilder builder{ + ownerValue, + nFTokenIDValue, + amountValue, + ownerNodeValue, + nFTokenOfferNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setDestination(destinationValue); + builder.setExpiration(expirationValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = ownerValue; + auto const actual = entry.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = nFTokenIDValue; + auto const actual = entry.getNFTokenID(); + expectEqualField(expected, actual, "sfNFTokenID"); + } + + { + auto const& expected = amountValue; + auto const actual = entry.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = nFTokenOfferNodeValue; + auto const actual = entry.getNFTokenOfferNode(); + expectEqualField(expected, actual, "sfNFTokenOfferNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = destinationValue; + auto const actualOpt = entry.getDestination(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDestination"); + EXPECT_TRUE(entry.hasDestination()); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = entry.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(entry.hasExpiration()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(NFTokenOfferTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const nFTokenIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const nFTokenOfferNodeValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + auto const expirationValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(NFTokenOffer::entryType, index); + + sle->at(sfOwner) = ownerValue; + sle->at(sfNFTokenID) = nFTokenIDValue; + sle->at(sfAmount) = amountValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfNFTokenOfferNode) = nFTokenOfferNodeValue; + sle->at(sfDestination) = destinationValue; + sle->at(sfExpiration) = expirationValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + NFTokenOfferBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + NFTokenOffer entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = ownerValue; + + auto const fromSle = entryFromSle.getOwner(); + auto const fromBuilder = entryFromBuilder.getOwner(); + + expectEqualField(expected, fromSle, "sfOwner"); + expectEqualField(expected, fromBuilder, "sfOwner"); + } + + { + auto const& expected = nFTokenIDValue; + + auto const fromSle = entryFromSle.getNFTokenID(); + auto const fromBuilder = entryFromBuilder.getNFTokenID(); + + expectEqualField(expected, fromSle, "sfNFTokenID"); + expectEqualField(expected, fromBuilder, "sfNFTokenID"); + } + + { + auto const& expected = amountValue; + + auto const fromSle = entryFromSle.getAmount(); + auto const fromBuilder = entryFromBuilder.getAmount(); + + expectEqualField(expected, fromSle, "sfAmount"); + expectEqualField(expected, fromBuilder, "sfAmount"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = nFTokenOfferNodeValue; + + auto const fromSle = entryFromSle.getNFTokenOfferNode(); + auto const fromBuilder = entryFromBuilder.getNFTokenOfferNode(); + + expectEqualField(expected, fromSle, "sfNFTokenOfferNode"); + expectEqualField(expected, fromBuilder, "sfNFTokenOfferNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = destinationValue; + + auto const fromSleOpt = entryFromSle.getDestination(); + auto const fromBuilderOpt = entryFromBuilder.getDestination(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDestination"); + expectEqualField(expected, *fromBuilderOpt, "sfDestination"); + } + + { + auto const& expected = expirationValue; + + auto const fromSleOpt = entryFromSle.getExpiration(); + auto const fromBuilderOpt = entryFromBuilder.getExpiration(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfExpiration"); + expectEqualField(expected, *fromBuilderOpt, "sfExpiration"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(NFTokenOfferTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(NFTokenOffer{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(NFTokenOfferTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(NFTokenOfferBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(NFTokenOfferTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const nFTokenIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const nFTokenOfferNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + NFTokenOfferBuilder builder{ + ownerValue, + nFTokenIDValue, + amountValue, + ownerNodeValue, + nFTokenOfferNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasDestination()); + EXPECT_FALSE(entry.getDestination().has_value()); + EXPECT_FALSE(entry.hasExpiration()); + EXPECT_FALSE(entry.getExpiration().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenPageTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenPageTests.cpp new file mode 100644 index 0000000000..5909050169 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenPageTests.cpp @@ -0,0 +1,236 @@ +// Auto-generated unit tests for ledger entry NFTokenPage + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(NFTokenPageTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const previousPageMinValue = canonical_UINT256(); + auto const nextPageMinValue = canonical_UINT256(); + auto const nFTokensValue = canonical_ARRAY(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + NFTokenPageBuilder builder{ + nFTokensValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setPreviousPageMin(previousPageMinValue); + builder.setNextPageMin(nextPageMinValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = nFTokensValue; + auto const actual = entry.getNFTokens(); + expectEqualField(expected, actual, "sfNFTokens"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = previousPageMinValue; + auto const actualOpt = entry.getPreviousPageMin(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousPageMin"); + EXPECT_TRUE(entry.hasPreviousPageMin()); + } + + { + auto const& expected = nextPageMinValue; + auto const actualOpt = entry.getNextPageMin(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfNextPageMin"); + EXPECT_TRUE(entry.hasNextPageMin()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(NFTokenPageTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const previousPageMinValue = canonical_UINT256(); + auto const nextPageMinValue = canonical_UINT256(); + auto const nFTokensValue = canonical_ARRAY(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(NFTokenPage::entryType, index); + + sle->at(sfPreviousPageMin) = previousPageMinValue; + sle->at(sfNextPageMin) = nextPageMinValue; + sle->setFieldArray(sfNFTokens, nFTokensValue); + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + NFTokenPageBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + NFTokenPage entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = nFTokensValue; + + auto const fromSle = entryFromSle.getNFTokens(); + auto const fromBuilder = entryFromBuilder.getNFTokens(); + + expectEqualField(expected, fromSle, "sfNFTokens"); + expectEqualField(expected, fromBuilder, "sfNFTokens"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = previousPageMinValue; + + auto const fromSleOpt = entryFromSle.getPreviousPageMin(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousPageMin(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousPageMin"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousPageMin"); + } + + { + auto const& expected = nextPageMinValue; + + auto const fromSleOpt = entryFromSle.getNextPageMin(); + auto const fromBuilderOpt = entryFromBuilder.getNextPageMin(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfNextPageMin"); + expectEqualField(expected, *fromBuilderOpt, "sfNextPageMin"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(NFTokenPageTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(NFTokenPage{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(NFTokenPageTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(NFTokenPageBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(NFTokenPageTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const nFTokensValue = canonical_ARRAY(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + NFTokenPageBuilder builder{ + nFTokensValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasPreviousPageMin()); + EXPECT_FALSE(entry.getPreviousPageMin().has_value()); + EXPECT_FALSE(entry.hasNextPageMin()); + EXPECT_FALSE(entry.getNextPageMin().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/NegativeUNLTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/NegativeUNLTests.cpp new file mode 100644 index 0000000000..48ff060c11 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/NegativeUNLTests.cpp @@ -0,0 +1,251 @@ +// Auto-generated unit tests for ledger entry NegativeUNL + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(NegativeUNLTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const disabledValidatorsValue = canonical_ARRAY(); + auto const validatorToDisableValue = canonical_VL(); + auto const validatorToReEnableValue = canonical_VL(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + NegativeUNLBuilder builder{ + }; + + builder.setDisabledValidators(disabledValidatorsValue); + builder.setValidatorToDisable(validatorToDisableValue); + builder.setValidatorToReEnable(validatorToReEnableValue); + builder.setPreviousTxnID(previousTxnIDValue); + builder.setPreviousTxnLgrSeq(previousTxnLgrSeqValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = disabledValidatorsValue; + auto const actualOpt = entry.getDisabledValidators(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDisabledValidators"); + EXPECT_TRUE(entry.hasDisabledValidators()); + } + + { + auto const& expected = validatorToDisableValue; + auto const actualOpt = entry.getValidatorToDisable(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfValidatorToDisable"); + EXPECT_TRUE(entry.hasValidatorToDisable()); + } + + { + auto const& expected = validatorToReEnableValue; + auto const actualOpt = entry.getValidatorToReEnable(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfValidatorToReEnable"); + EXPECT_TRUE(entry.hasValidatorToReEnable()); + } + + { + auto const& expected = previousTxnIDValue; + auto const actualOpt = entry.getPreviousTxnID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnID"); + EXPECT_TRUE(entry.hasPreviousTxnID()); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actualOpt = entry.getPreviousTxnLgrSeq(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnLgrSeq"); + EXPECT_TRUE(entry.hasPreviousTxnLgrSeq()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(NegativeUNLTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const disabledValidatorsValue = canonical_ARRAY(); + auto const validatorToDisableValue = canonical_VL(); + auto const validatorToReEnableValue = canonical_VL(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(NegativeUNL::entryType, index); + + sle->setFieldArray(sfDisabledValidators, disabledValidatorsValue); + sle->at(sfValidatorToDisable) = validatorToDisableValue; + sle->at(sfValidatorToReEnable) = validatorToReEnableValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + NegativeUNLBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + NegativeUNL entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = disabledValidatorsValue; + + auto const fromSleOpt = entryFromSle.getDisabledValidators(); + auto const fromBuilderOpt = entryFromBuilder.getDisabledValidators(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDisabledValidators"); + expectEqualField(expected, *fromBuilderOpt, "sfDisabledValidators"); + } + + { + auto const& expected = validatorToDisableValue; + + auto const fromSleOpt = entryFromSle.getValidatorToDisable(); + auto const fromBuilderOpt = entryFromBuilder.getValidatorToDisable(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfValidatorToDisable"); + expectEqualField(expected, *fromBuilderOpt, "sfValidatorToDisable"); + } + + { + auto const& expected = validatorToReEnableValue; + + auto const fromSleOpt = entryFromSle.getValidatorToReEnable(); + auto const fromBuilderOpt = entryFromBuilder.getValidatorToReEnable(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfValidatorToReEnable"); + expectEqualField(expected, *fromBuilderOpt, "sfValidatorToReEnable"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnID(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnID"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnLgrSeq(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(NegativeUNLTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(NegativeUNL{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(NegativeUNLTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(NegativeUNLBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(NegativeUNLTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + + NegativeUNLBuilder builder{ + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasDisabledValidators()); + EXPECT_FALSE(entry.getDisabledValidators().has_value()); + EXPECT_FALSE(entry.hasValidatorToDisable()); + EXPECT_FALSE(entry.getValidatorToDisable().has_value()); + EXPECT_FALSE(entry.hasValidatorToReEnable()); + EXPECT_FALSE(entry.getValidatorToReEnable().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnID()); + EXPECT_FALSE(entry.getPreviousTxnID().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnLgrSeq()); + EXPECT_FALSE(entry.getPreviousTxnLgrSeq().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/OfferTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/OfferTests.cpp new file mode 100644 index 0000000000..5cb1bbd804 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/OfferTests.cpp @@ -0,0 +1,395 @@ +// Auto-generated unit tests for ledger entry Offer + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(OfferTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const takerPaysValue = canonical_AMOUNT(); + auto const takerGetsValue = canonical_AMOUNT(); + auto const bookDirectoryValue = canonical_UINT256(); + auto const bookNodeValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const expirationValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + auto const additionalBooksValue = canonical_ARRAY(); + + OfferBuilder builder{ + accountValue, + sequenceValue, + takerPaysValue, + takerGetsValue, + bookDirectoryValue, + bookNodeValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setExpiration(expirationValue); + builder.setDomainID(domainIDValue); + builder.setAdditionalBooks(additionalBooksValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = sequenceValue; + auto const actual = entry.getSequence(); + expectEqualField(expected, actual, "sfSequence"); + } + + { + auto const& expected = takerPaysValue; + auto const actual = entry.getTakerPays(); + expectEqualField(expected, actual, "sfTakerPays"); + } + + { + auto const& expected = takerGetsValue; + auto const actual = entry.getTakerGets(); + expectEqualField(expected, actual, "sfTakerGets"); + } + + { + auto const& expected = bookDirectoryValue; + auto const actual = entry.getBookDirectory(); + expectEqualField(expected, actual, "sfBookDirectory"); + } + + { + auto const& expected = bookNodeValue; + auto const actual = entry.getBookNode(); + expectEqualField(expected, actual, "sfBookNode"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = entry.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(entry.hasExpiration()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = entry.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(entry.hasDomainID()); + } + + { + auto const& expected = additionalBooksValue; + auto const actualOpt = entry.getAdditionalBooks(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAdditionalBooks"); + EXPECT_TRUE(entry.hasAdditionalBooks()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(OfferTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const takerPaysValue = canonical_AMOUNT(); + auto const takerGetsValue = canonical_AMOUNT(); + auto const bookDirectoryValue = canonical_UINT256(); + auto const bookNodeValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const expirationValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + auto const additionalBooksValue = canonical_ARRAY(); + + auto sle = std::make_shared(Offer::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfTakerPays) = takerPaysValue; + sle->at(sfTakerGets) = takerGetsValue; + sle->at(sfBookDirectory) = bookDirectoryValue; + sle->at(sfBookNode) = bookNodeValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfExpiration) = expirationValue; + sle->at(sfDomainID) = domainIDValue; + sle->setFieldArray(sfAdditionalBooks, additionalBooksValue); + + OfferBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Offer entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSle = entryFromSle.getSequence(); + auto const fromBuilder = entryFromBuilder.getSequence(); + + expectEqualField(expected, fromSle, "sfSequence"); + expectEqualField(expected, fromBuilder, "sfSequence"); + } + + { + auto const& expected = takerPaysValue; + + auto const fromSle = entryFromSle.getTakerPays(); + auto const fromBuilder = entryFromBuilder.getTakerPays(); + + expectEqualField(expected, fromSle, "sfTakerPays"); + expectEqualField(expected, fromBuilder, "sfTakerPays"); + } + + { + auto const& expected = takerGetsValue; + + auto const fromSle = entryFromSle.getTakerGets(); + auto const fromBuilder = entryFromBuilder.getTakerGets(); + + expectEqualField(expected, fromSle, "sfTakerGets"); + expectEqualField(expected, fromBuilder, "sfTakerGets"); + } + + { + auto const& expected = bookDirectoryValue; + + auto const fromSle = entryFromSle.getBookDirectory(); + auto const fromBuilder = entryFromBuilder.getBookDirectory(); + + expectEqualField(expected, fromSle, "sfBookDirectory"); + expectEqualField(expected, fromBuilder, "sfBookDirectory"); + } + + { + auto const& expected = bookNodeValue; + + auto const fromSle = entryFromSle.getBookNode(); + auto const fromBuilder = entryFromBuilder.getBookNode(); + + expectEqualField(expected, fromSle, "sfBookNode"); + expectEqualField(expected, fromBuilder, "sfBookNode"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = expirationValue; + + auto const fromSleOpt = entryFromSle.getExpiration(); + auto const fromBuilderOpt = entryFromBuilder.getExpiration(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfExpiration"); + expectEqualField(expected, *fromBuilderOpt, "sfExpiration"); + } + + { + auto const& expected = domainIDValue; + + auto const fromSleOpt = entryFromSle.getDomainID(); + auto const fromBuilderOpt = entryFromBuilder.getDomainID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDomainID"); + expectEqualField(expected, *fromBuilderOpt, "sfDomainID"); + } + + { + auto const& expected = additionalBooksValue; + + auto const fromSleOpt = entryFromSle.getAdditionalBooks(); + auto const fromBuilderOpt = entryFromBuilder.getAdditionalBooks(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAdditionalBooks"); + expectEqualField(expected, *fromBuilderOpt, "sfAdditionalBooks"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(OfferTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Offer{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(OfferTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(OfferBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(OfferTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const takerPaysValue = canonical_AMOUNT(); + auto const takerGetsValue = canonical_AMOUNT(); + auto const bookDirectoryValue = canonical_UINT256(); + auto const bookNodeValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + OfferBuilder builder{ + accountValue, + sequenceValue, + takerPaysValue, + takerGetsValue, + bookDirectoryValue, + bookNodeValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasExpiration()); + EXPECT_FALSE(entry.getExpiration().has_value()); + EXPECT_FALSE(entry.hasDomainID()); + EXPECT_FALSE(entry.getDomainID().has_value()); + EXPECT_FALSE(entry.hasAdditionalBooks()); + EXPECT_FALSE(entry.getAdditionalBooks().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/OracleTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/OracleTests.cpp new file mode 100644 index 0000000000..837fd576bf --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/OracleTests.cpp @@ -0,0 +1,346 @@ +// Auto-generated unit tests for ledger entry Oracle + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(OracleTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const oracleDocumentIDValue = canonical_UINT32(); + auto const providerValue = canonical_VL(); + auto const priceDataSeriesValue = canonical_ARRAY(); + auto const assetClassValue = canonical_VL(); + auto const lastUpdateTimeValue = canonical_UINT32(); + auto const uRIValue = canonical_VL(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + OracleBuilder builder{ + ownerValue, + providerValue, + priceDataSeriesValue, + assetClassValue, + lastUpdateTimeValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setOracleDocumentID(oracleDocumentIDValue); + builder.setURI(uRIValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = ownerValue; + auto const actual = entry.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = providerValue; + auto const actual = entry.getProvider(); + expectEqualField(expected, actual, "sfProvider"); + } + + { + auto const& expected = priceDataSeriesValue; + auto const actual = entry.getPriceDataSeries(); + expectEqualField(expected, actual, "sfPriceDataSeries"); + } + + { + auto const& expected = assetClassValue; + auto const actual = entry.getAssetClass(); + expectEqualField(expected, actual, "sfAssetClass"); + } + + { + auto const& expected = lastUpdateTimeValue; + auto const actual = entry.getLastUpdateTime(); + expectEqualField(expected, actual, "sfLastUpdateTime"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = oracleDocumentIDValue; + auto const actualOpt = entry.getOracleDocumentID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfOracleDocumentID"); + EXPECT_TRUE(entry.hasOracleDocumentID()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = entry.getURI(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(entry.hasURI()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(OracleTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const oracleDocumentIDValue = canonical_UINT32(); + auto const providerValue = canonical_VL(); + auto const priceDataSeriesValue = canonical_ARRAY(); + auto const assetClassValue = canonical_VL(); + auto const lastUpdateTimeValue = canonical_UINT32(); + auto const uRIValue = canonical_VL(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(Oracle::entryType, index); + + sle->at(sfOwner) = ownerValue; + sle->at(sfOracleDocumentID) = oracleDocumentIDValue; + sle->at(sfProvider) = providerValue; + sle->setFieldArray(sfPriceDataSeries, priceDataSeriesValue); + sle->at(sfAssetClass) = assetClassValue; + sle->at(sfLastUpdateTime) = lastUpdateTimeValue; + sle->at(sfURI) = uRIValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + OracleBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Oracle entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = ownerValue; + + auto const fromSle = entryFromSle.getOwner(); + auto const fromBuilder = entryFromBuilder.getOwner(); + + expectEqualField(expected, fromSle, "sfOwner"); + expectEqualField(expected, fromBuilder, "sfOwner"); + } + + { + auto const& expected = providerValue; + + auto const fromSle = entryFromSle.getProvider(); + auto const fromBuilder = entryFromBuilder.getProvider(); + + expectEqualField(expected, fromSle, "sfProvider"); + expectEqualField(expected, fromBuilder, "sfProvider"); + } + + { + auto const& expected = priceDataSeriesValue; + + auto const fromSle = entryFromSle.getPriceDataSeries(); + auto const fromBuilder = entryFromBuilder.getPriceDataSeries(); + + expectEqualField(expected, fromSle, "sfPriceDataSeries"); + expectEqualField(expected, fromBuilder, "sfPriceDataSeries"); + } + + { + auto const& expected = assetClassValue; + + auto const fromSle = entryFromSle.getAssetClass(); + auto const fromBuilder = entryFromBuilder.getAssetClass(); + + expectEqualField(expected, fromSle, "sfAssetClass"); + expectEqualField(expected, fromBuilder, "sfAssetClass"); + } + + { + auto const& expected = lastUpdateTimeValue; + + auto const fromSle = entryFromSle.getLastUpdateTime(); + auto const fromBuilder = entryFromBuilder.getLastUpdateTime(); + + expectEqualField(expected, fromSle, "sfLastUpdateTime"); + expectEqualField(expected, fromBuilder, "sfLastUpdateTime"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = oracleDocumentIDValue; + + auto const fromSleOpt = entryFromSle.getOracleDocumentID(); + auto const fromBuilderOpt = entryFromBuilder.getOracleDocumentID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfOracleDocumentID"); + expectEqualField(expected, *fromBuilderOpt, "sfOracleDocumentID"); + } + + { + auto const& expected = uRIValue; + + auto const fromSleOpt = entryFromSle.getURI(); + auto const fromBuilderOpt = entryFromBuilder.getURI(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfURI"); + expectEqualField(expected, *fromBuilderOpt, "sfURI"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(OracleTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Oracle{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(OracleTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(OracleBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(OracleTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const providerValue = canonical_VL(); + auto const priceDataSeriesValue = canonical_ARRAY(); + auto const assetClassValue = canonical_VL(); + auto const lastUpdateTimeValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + OracleBuilder builder{ + ownerValue, + providerValue, + priceDataSeriesValue, + assetClassValue, + lastUpdateTimeValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasOracleDocumentID()); + EXPECT_FALSE(entry.getOracleDocumentID().has_value()); + EXPECT_FALSE(entry.hasURI()); + EXPECT_FALSE(entry.getURI().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/PayChannelTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/PayChannelTests.cpp new file mode 100644 index 0000000000..7c31252369 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/PayChannelTests.cpp @@ -0,0 +1,476 @@ +// Auto-generated unit tests for ledger entry PayChannel + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(PayChannelTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const amountValue = canonical_AMOUNT(); + auto const balanceValue = canonical_AMOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const settleDelayValue = canonical_UINT32(); + auto const expirationValue = canonical_UINT32(); + auto const cancelAfterValue = canonical_UINT32(); + auto const sourceTagValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const destinationNodeValue = canonical_UINT64(); + + PayChannelBuilder builder{ + accountValue, + destinationValue, + amountValue, + balanceValue, + publicKeyValue, + settleDelayValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setSequence(sequenceValue); + builder.setExpiration(expirationValue); + builder.setCancelAfter(cancelAfterValue); + builder.setSourceTag(sourceTagValue); + builder.setDestinationTag(destinationTagValue); + builder.setDestinationNode(destinationNodeValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = destinationValue; + auto const actual = entry.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = entry.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = balanceValue; + auto const actual = entry.getBalance(); + expectEqualField(expected, actual, "sfBalance"); + } + + { + auto const& expected = publicKeyValue; + auto const actual = entry.getPublicKey(); + expectEqualField(expected, actual, "sfPublicKey"); + } + + { + auto const& expected = settleDelayValue; + auto const actual = entry.getSettleDelay(); + expectEqualField(expected, actual, "sfSettleDelay"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + auto const actualOpt = entry.getSequence(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfSequence"); + EXPECT_TRUE(entry.hasSequence()); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = entry.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(entry.hasExpiration()); + } + + { + auto const& expected = cancelAfterValue; + auto const actualOpt = entry.getCancelAfter(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfCancelAfter"); + EXPECT_TRUE(entry.hasCancelAfter()); + } + + { + auto const& expected = sourceTagValue; + auto const actualOpt = entry.getSourceTag(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfSourceTag"); + EXPECT_TRUE(entry.hasSourceTag()); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = entry.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + EXPECT_TRUE(entry.hasDestinationTag()); + } + + { + auto const& expected = destinationNodeValue; + auto const actualOpt = entry.getDestinationNode(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDestinationNode"); + EXPECT_TRUE(entry.hasDestinationNode()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(PayChannelTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const amountValue = canonical_AMOUNT(); + auto const balanceValue = canonical_AMOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const settleDelayValue = canonical_UINT32(); + auto const expirationValue = canonical_UINT32(); + auto const cancelAfterValue = canonical_UINT32(); + auto const sourceTagValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const destinationNodeValue = canonical_UINT64(); + + auto sle = std::make_shared(PayChannel::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfDestination) = destinationValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfAmount) = amountValue; + sle->at(sfBalance) = balanceValue; + sle->at(sfPublicKey) = publicKeyValue; + sle->at(sfSettleDelay) = settleDelayValue; + sle->at(sfExpiration) = expirationValue; + sle->at(sfCancelAfter) = cancelAfterValue; + sle->at(sfSourceTag) = sourceTagValue; + sle->at(sfDestinationTag) = destinationTagValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfDestinationNode) = destinationNodeValue; + + PayChannelBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + PayChannel entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = destinationValue; + + auto const fromSle = entryFromSle.getDestination(); + auto const fromBuilder = entryFromBuilder.getDestination(); + + expectEqualField(expected, fromSle, "sfDestination"); + expectEqualField(expected, fromBuilder, "sfDestination"); + } + + { + auto const& expected = amountValue; + + auto const fromSle = entryFromSle.getAmount(); + auto const fromBuilder = entryFromBuilder.getAmount(); + + expectEqualField(expected, fromSle, "sfAmount"); + expectEqualField(expected, fromBuilder, "sfAmount"); + } + + { + auto const& expected = balanceValue; + + auto const fromSle = entryFromSle.getBalance(); + auto const fromBuilder = entryFromBuilder.getBalance(); + + expectEqualField(expected, fromSle, "sfBalance"); + expectEqualField(expected, fromBuilder, "sfBalance"); + } + + { + auto const& expected = publicKeyValue; + + auto const fromSle = entryFromSle.getPublicKey(); + auto const fromBuilder = entryFromBuilder.getPublicKey(); + + expectEqualField(expected, fromSle, "sfPublicKey"); + expectEqualField(expected, fromBuilder, "sfPublicKey"); + } + + { + auto const& expected = settleDelayValue; + + auto const fromSle = entryFromSle.getSettleDelay(); + auto const fromBuilder = entryFromBuilder.getSettleDelay(); + + expectEqualField(expected, fromSle, "sfSettleDelay"); + expectEqualField(expected, fromBuilder, "sfSettleDelay"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSleOpt = entryFromSle.getSequence(); + auto const fromBuilderOpt = entryFromBuilder.getSequence(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfSequence"); + expectEqualField(expected, *fromBuilderOpt, "sfSequence"); + } + + { + auto const& expected = expirationValue; + + auto const fromSleOpt = entryFromSle.getExpiration(); + auto const fromBuilderOpt = entryFromBuilder.getExpiration(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfExpiration"); + expectEqualField(expected, *fromBuilderOpt, "sfExpiration"); + } + + { + auto const& expected = cancelAfterValue; + + auto const fromSleOpt = entryFromSle.getCancelAfter(); + auto const fromBuilderOpt = entryFromBuilder.getCancelAfter(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfCancelAfter"); + expectEqualField(expected, *fromBuilderOpt, "sfCancelAfter"); + } + + { + auto const& expected = sourceTagValue; + + auto const fromSleOpt = entryFromSle.getSourceTag(); + auto const fromBuilderOpt = entryFromBuilder.getSourceTag(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfSourceTag"); + expectEqualField(expected, *fromBuilderOpt, "sfSourceTag"); + } + + { + auto const& expected = destinationTagValue; + + auto const fromSleOpt = entryFromSle.getDestinationTag(); + auto const fromBuilderOpt = entryFromBuilder.getDestinationTag(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDestinationTag"); + expectEqualField(expected, *fromBuilderOpt, "sfDestinationTag"); + } + + { + auto const& expected = destinationNodeValue; + + auto const fromSleOpt = entryFromSle.getDestinationNode(); + auto const fromBuilderOpt = entryFromBuilder.getDestinationNode(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDestinationNode"); + expectEqualField(expected, *fromBuilderOpt, "sfDestinationNode"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(PayChannelTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(PayChannel{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(PayChannelTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(PayChannelBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(PayChannelTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const balanceValue = canonical_AMOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const settleDelayValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + PayChannelBuilder builder{ + accountValue, + destinationValue, + amountValue, + balanceValue, + publicKeyValue, + settleDelayValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasSequence()); + EXPECT_FALSE(entry.getSequence().has_value()); + EXPECT_FALSE(entry.hasExpiration()); + EXPECT_FALSE(entry.getExpiration().has_value()); + EXPECT_FALSE(entry.hasCancelAfter()); + EXPECT_FALSE(entry.getCancelAfter().has_value()); + EXPECT_FALSE(entry.hasSourceTag()); + EXPECT_FALSE(entry.getSourceTag().has_value()); + EXPECT_FALSE(entry.hasDestinationTag()); + EXPECT_FALSE(entry.getDestinationTag().has_value()); + EXPECT_FALSE(entry.hasDestinationNode()); + EXPECT_FALSE(entry.getDestinationNode().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/PermissionedDomainTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/PermissionedDomainTests.cpp new file mode 100644 index 0000000000..c448d064cf --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/PermissionedDomainTests.cpp @@ -0,0 +1,223 @@ +// Auto-generated unit tests for ledger entry PermissionedDomain + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(PermissionedDomainTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const acceptedCredentialsValue = canonical_ARRAY(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + PermissionedDomainBuilder builder{ + ownerValue, + sequenceValue, + acceptedCredentialsValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = ownerValue; + auto const actual = entry.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = sequenceValue; + auto const actual = entry.getSequence(); + expectEqualField(expected, actual, "sfSequence"); + } + + { + auto const& expected = acceptedCredentialsValue; + auto const actual = entry.getAcceptedCredentials(); + expectEqualField(expected, actual, "sfAcceptedCredentials"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(PermissionedDomainTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const acceptedCredentialsValue = canonical_ARRAY(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(PermissionedDomain::entryType, index); + + sle->at(sfOwner) = ownerValue; + sle->at(sfSequence) = sequenceValue; + sle->setFieldArray(sfAcceptedCredentials, acceptedCredentialsValue); + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + PermissionedDomainBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + PermissionedDomain entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = ownerValue; + + auto const fromSle = entryFromSle.getOwner(); + auto const fromBuilder = entryFromBuilder.getOwner(); + + expectEqualField(expected, fromSle, "sfOwner"); + expectEqualField(expected, fromBuilder, "sfOwner"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSle = entryFromSle.getSequence(); + auto const fromBuilder = entryFromBuilder.getSequence(); + + expectEqualField(expected, fromSle, "sfSequence"); + expectEqualField(expected, fromBuilder, "sfSequence"); + } + + { + auto const& expected = acceptedCredentialsValue; + + auto const fromSle = entryFromSle.getAcceptedCredentials(); + auto const fromBuilder = entryFromBuilder.getAcceptedCredentials(); + + expectEqualField(expected, fromSle, "sfAcceptedCredentials"); + expectEqualField(expected, fromBuilder, "sfAcceptedCredentials"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(PermissionedDomainTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(PermissionedDomain{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(PermissionedDomainTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(PermissionedDomainBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/RippleStateTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/RippleStateTests.cpp new file mode 100644 index 0000000000..a51ce55f6f --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/RippleStateTests.cpp @@ -0,0 +1,388 @@ +// Auto-generated unit tests for ledger entry RippleState + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(RippleStateTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const balanceValue = canonical_AMOUNT(); + auto const lowLimitValue = canonical_AMOUNT(); + auto const highLimitValue = canonical_AMOUNT(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const lowNodeValue = canonical_UINT64(); + auto const lowQualityInValue = canonical_UINT32(); + auto const lowQualityOutValue = canonical_UINT32(); + auto const highNodeValue = canonical_UINT64(); + auto const highQualityInValue = canonical_UINT32(); + auto const highQualityOutValue = canonical_UINT32(); + + RippleStateBuilder builder{ + balanceValue, + lowLimitValue, + highLimitValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setLowNode(lowNodeValue); + builder.setLowQualityIn(lowQualityInValue); + builder.setLowQualityOut(lowQualityOutValue); + builder.setHighNode(highNodeValue); + builder.setHighQualityIn(highQualityInValue); + builder.setHighQualityOut(highQualityOutValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = balanceValue; + auto const actual = entry.getBalance(); + expectEqualField(expected, actual, "sfBalance"); + } + + { + auto const& expected = lowLimitValue; + auto const actual = entry.getLowLimit(); + expectEqualField(expected, actual, "sfLowLimit"); + } + + { + auto const& expected = highLimitValue; + auto const actual = entry.getHighLimit(); + expectEqualField(expected, actual, "sfHighLimit"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = lowNodeValue; + auto const actualOpt = entry.getLowNode(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLowNode"); + EXPECT_TRUE(entry.hasLowNode()); + } + + { + auto const& expected = lowQualityInValue; + auto const actualOpt = entry.getLowQualityIn(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLowQualityIn"); + EXPECT_TRUE(entry.hasLowQualityIn()); + } + + { + auto const& expected = lowQualityOutValue; + auto const actualOpt = entry.getLowQualityOut(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLowQualityOut"); + EXPECT_TRUE(entry.hasLowQualityOut()); + } + + { + auto const& expected = highNodeValue; + auto const actualOpt = entry.getHighNode(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfHighNode"); + EXPECT_TRUE(entry.hasHighNode()); + } + + { + auto const& expected = highQualityInValue; + auto const actualOpt = entry.getHighQualityIn(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfHighQualityIn"); + EXPECT_TRUE(entry.hasHighQualityIn()); + } + + { + auto const& expected = highQualityOutValue; + auto const actualOpt = entry.getHighQualityOut(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfHighQualityOut"); + EXPECT_TRUE(entry.hasHighQualityOut()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(RippleStateTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const balanceValue = canonical_AMOUNT(); + auto const lowLimitValue = canonical_AMOUNT(); + auto const highLimitValue = canonical_AMOUNT(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const lowNodeValue = canonical_UINT64(); + auto const lowQualityInValue = canonical_UINT32(); + auto const lowQualityOutValue = canonical_UINT32(); + auto const highNodeValue = canonical_UINT64(); + auto const highQualityInValue = canonical_UINT32(); + auto const highQualityOutValue = canonical_UINT32(); + + auto sle = std::make_shared(RippleState::entryType, index); + + sle->at(sfBalance) = balanceValue; + sle->at(sfLowLimit) = lowLimitValue; + sle->at(sfHighLimit) = highLimitValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfLowNode) = lowNodeValue; + sle->at(sfLowQualityIn) = lowQualityInValue; + sle->at(sfLowQualityOut) = lowQualityOutValue; + sle->at(sfHighNode) = highNodeValue; + sle->at(sfHighQualityIn) = highQualityInValue; + sle->at(sfHighQualityOut) = highQualityOutValue; + + RippleStateBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + RippleState entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = balanceValue; + + auto const fromSle = entryFromSle.getBalance(); + auto const fromBuilder = entryFromBuilder.getBalance(); + + expectEqualField(expected, fromSle, "sfBalance"); + expectEqualField(expected, fromBuilder, "sfBalance"); + } + + { + auto const& expected = lowLimitValue; + + auto const fromSle = entryFromSle.getLowLimit(); + auto const fromBuilder = entryFromBuilder.getLowLimit(); + + expectEqualField(expected, fromSle, "sfLowLimit"); + expectEqualField(expected, fromBuilder, "sfLowLimit"); + } + + { + auto const& expected = highLimitValue; + + auto const fromSle = entryFromSle.getHighLimit(); + auto const fromBuilder = entryFromBuilder.getHighLimit(); + + expectEqualField(expected, fromSle, "sfHighLimit"); + expectEqualField(expected, fromBuilder, "sfHighLimit"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = lowNodeValue; + + auto const fromSleOpt = entryFromSle.getLowNode(); + auto const fromBuilderOpt = entryFromBuilder.getLowNode(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLowNode"); + expectEqualField(expected, *fromBuilderOpt, "sfLowNode"); + } + + { + auto const& expected = lowQualityInValue; + + auto const fromSleOpt = entryFromSle.getLowQualityIn(); + auto const fromBuilderOpt = entryFromBuilder.getLowQualityIn(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLowQualityIn"); + expectEqualField(expected, *fromBuilderOpt, "sfLowQualityIn"); + } + + { + auto const& expected = lowQualityOutValue; + + auto const fromSleOpt = entryFromSle.getLowQualityOut(); + auto const fromBuilderOpt = entryFromBuilder.getLowQualityOut(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLowQualityOut"); + expectEqualField(expected, *fromBuilderOpt, "sfLowQualityOut"); + } + + { + auto const& expected = highNodeValue; + + auto const fromSleOpt = entryFromSle.getHighNode(); + auto const fromBuilderOpt = entryFromBuilder.getHighNode(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfHighNode"); + expectEqualField(expected, *fromBuilderOpt, "sfHighNode"); + } + + { + auto const& expected = highQualityInValue; + + auto const fromSleOpt = entryFromSle.getHighQualityIn(); + auto const fromBuilderOpt = entryFromBuilder.getHighQualityIn(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfHighQualityIn"); + expectEqualField(expected, *fromBuilderOpt, "sfHighQualityIn"); + } + + { + auto const& expected = highQualityOutValue; + + auto const fromSleOpt = entryFromSle.getHighQualityOut(); + auto const fromBuilderOpt = entryFromBuilder.getHighQualityOut(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfHighQualityOut"); + expectEqualField(expected, *fromBuilderOpt, "sfHighQualityOut"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(RippleStateTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(RippleState{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(RippleStateTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(RippleStateBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(RippleStateTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const balanceValue = canonical_AMOUNT(); + auto const lowLimitValue = canonical_AMOUNT(); + auto const highLimitValue = canonical_AMOUNT(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + RippleStateBuilder builder{ + balanceValue, + lowLimitValue, + highLimitValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasLowNode()); + EXPECT_FALSE(entry.getLowNode().has_value()); + EXPECT_FALSE(entry.hasLowQualityIn()); + EXPECT_FALSE(entry.getLowQualityIn().has_value()); + EXPECT_FALSE(entry.hasLowQualityOut()); + EXPECT_FALSE(entry.getLowQualityOut().has_value()); + EXPECT_FALSE(entry.hasHighNode()); + EXPECT_FALSE(entry.getHighNode().has_value()); + EXPECT_FALSE(entry.hasHighQualityIn()); + EXPECT_FALSE(entry.getHighQualityIn().has_value()); + EXPECT_FALSE(entry.hasHighQualityOut()); + EXPECT_FALSE(entry.getHighQualityOut().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/SignerListTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/SignerListTests.cpp new file mode 100644 index 0000000000..5ec2c4e10d --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/SignerListTests.cpp @@ -0,0 +1,275 @@ +// Auto-generated unit tests for ledger entry SignerList + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(SignerListTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const signerQuorumValue = canonical_UINT32(); + auto const signerEntriesValue = canonical_ARRAY(); + auto const signerListIDValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + SignerListBuilder builder{ + ownerNodeValue, + signerQuorumValue, + signerEntriesValue, + signerListIDValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setOwner(ownerValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = signerQuorumValue; + auto const actual = entry.getSignerQuorum(); + expectEqualField(expected, actual, "sfSignerQuorum"); + } + + { + auto const& expected = signerEntriesValue; + auto const actual = entry.getSignerEntries(); + expectEqualField(expected, actual, "sfSignerEntries"); + } + + { + auto const& expected = signerListIDValue; + auto const actual = entry.getSignerListID(); + expectEqualField(expected, actual, "sfSignerListID"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = ownerValue; + auto const actualOpt = entry.getOwner(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfOwner"); + EXPECT_TRUE(entry.hasOwner()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(SignerListTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const signerQuorumValue = canonical_UINT32(); + auto const signerEntriesValue = canonical_ARRAY(); + auto const signerListIDValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(SignerList::entryType, index); + + sle->at(sfOwner) = ownerValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfSignerQuorum) = signerQuorumValue; + sle->setFieldArray(sfSignerEntries, signerEntriesValue); + sle->at(sfSignerListID) = signerListIDValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + SignerListBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + SignerList entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = signerQuorumValue; + + auto const fromSle = entryFromSle.getSignerQuorum(); + auto const fromBuilder = entryFromBuilder.getSignerQuorum(); + + expectEqualField(expected, fromSle, "sfSignerQuorum"); + expectEqualField(expected, fromBuilder, "sfSignerQuorum"); + } + + { + auto const& expected = signerEntriesValue; + + auto const fromSle = entryFromSle.getSignerEntries(); + auto const fromBuilder = entryFromBuilder.getSignerEntries(); + + expectEqualField(expected, fromSle, "sfSignerEntries"); + expectEqualField(expected, fromBuilder, "sfSignerEntries"); + } + + { + auto const& expected = signerListIDValue; + + auto const fromSle = entryFromSle.getSignerListID(); + auto const fromBuilder = entryFromBuilder.getSignerListID(); + + expectEqualField(expected, fromSle, "sfSignerListID"); + expectEqualField(expected, fromBuilder, "sfSignerListID"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = ownerValue; + + auto const fromSleOpt = entryFromSle.getOwner(); + auto const fromBuilderOpt = entryFromBuilder.getOwner(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfOwner"); + expectEqualField(expected, *fromBuilderOpt, "sfOwner"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(SignerListTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(SignerList{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(SignerListTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(SignerListBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(SignerListTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const ownerNodeValue = canonical_UINT64(); + auto const signerQuorumValue = canonical_UINT32(); + auto const signerEntriesValue = canonical_ARRAY(); + auto const signerListIDValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + SignerListBuilder builder{ + ownerNodeValue, + signerQuorumValue, + signerEntriesValue, + signerListIDValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasOwner()); + EXPECT_FALSE(entry.getOwner().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/TicketTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/TicketTests.cpp new file mode 100644 index 0000000000..1ac00288f0 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/TicketTests.cpp @@ -0,0 +1,209 @@ +// Auto-generated unit tests for ledger entry Ticket + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(TicketTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const ticketSequenceValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + TicketBuilder builder{ + accountValue, + ownerNodeValue, + ticketSequenceValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = ticketSequenceValue; + auto const actual = entry.getTicketSequence(); + expectEqualField(expected, actual, "sfTicketSequence"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(TicketTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const ticketSequenceValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(Ticket::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfTicketSequence) = ticketSequenceValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + TicketBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Ticket entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = ticketSequenceValue; + + auto const fromSle = entryFromSle.getTicketSequence(); + auto const fromBuilder = entryFromBuilder.getTicketSequence(); + + expectEqualField(expected, fromSle, "sfTicketSequence"); + expectEqualField(expected, fromBuilder, "sfTicketSequence"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(TicketTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + CheckBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_ACCOUNT(), + canonical_AMOUNT(), + canonical_UINT32(), + canonical_UINT64(), + canonical_UINT64(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Ticket{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(TicketTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + CheckBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_ACCOUNT(), + canonical_AMOUNT(), + canonical_UINT32(), + canonical_UINT64(), + canonical_UINT64(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(TicketBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/VaultTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/VaultTests.cpp new file mode 100644 index 0000000000..2697924d37 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/VaultTests.cpp @@ -0,0 +1,476 @@ +// Auto-generated unit tests for ledger entry Vault + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(VaultTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const ownerValue = canonical_ACCOUNT(); + auto const accountValue = canonical_ACCOUNT(); + auto const dataValue = canonical_VL(); + auto const assetValue = canonical_ISSUE(); + auto const assetsTotalValue = canonical_NUMBER(); + auto const assetsAvailableValue = canonical_NUMBER(); + auto const assetsMaximumValue = canonical_NUMBER(); + auto const lossUnrealizedValue = canonical_NUMBER(); + auto const shareMPTIDValue = canonical_UINT192(); + auto const withdrawalPolicyValue = canonical_UINT8(); + auto const scaleValue = canonical_UINT8(); + + VaultBuilder builder{ + previousTxnIDValue, + previousTxnLgrSeqValue, + sequenceValue, + ownerNodeValue, + ownerValue, + accountValue, + assetValue, + shareMPTIDValue, + withdrawalPolicyValue + }; + + builder.setData(dataValue); + builder.setAssetsTotal(assetsTotalValue); + builder.setAssetsAvailable(assetsAvailableValue); + builder.setAssetsMaximum(assetsMaximumValue); + builder.setLossUnrealized(lossUnrealizedValue); + builder.setScale(scaleValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + auto const actual = entry.getSequence(); + expectEqualField(expected, actual, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = ownerValue; + auto const actual = entry.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = assetValue; + auto const actual = entry.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = shareMPTIDValue; + auto const actual = entry.getShareMPTID(); + expectEqualField(expected, actual, "sfShareMPTID"); + } + + { + auto const& expected = withdrawalPolicyValue; + auto const actual = entry.getWithdrawalPolicy(); + expectEqualField(expected, actual, "sfWithdrawalPolicy"); + } + + { + auto const& expected = dataValue; + auto const actualOpt = entry.getData(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfData"); + EXPECT_TRUE(entry.hasData()); + } + + { + auto const& expected = assetsTotalValue; + auto const actualOpt = entry.getAssetsTotal(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAssetsTotal"); + EXPECT_TRUE(entry.hasAssetsTotal()); + } + + { + auto const& expected = assetsAvailableValue; + auto const actualOpt = entry.getAssetsAvailable(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAssetsAvailable"); + EXPECT_TRUE(entry.hasAssetsAvailable()); + } + + { + auto const& expected = assetsMaximumValue; + auto const actualOpt = entry.getAssetsMaximum(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAssetsMaximum"); + EXPECT_TRUE(entry.hasAssetsMaximum()); + } + + { + auto const& expected = lossUnrealizedValue; + auto const actualOpt = entry.getLossUnrealized(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLossUnrealized"); + EXPECT_TRUE(entry.hasLossUnrealized()); + } + + { + auto const& expected = scaleValue; + auto const actualOpt = entry.getScale(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfScale"); + EXPECT_TRUE(entry.hasScale()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(VaultTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const ownerValue = canonical_ACCOUNT(); + auto const accountValue = canonical_ACCOUNT(); + auto const dataValue = canonical_VL(); + auto const assetValue = canonical_ISSUE(); + auto const assetsTotalValue = canonical_NUMBER(); + auto const assetsAvailableValue = canonical_NUMBER(); + auto const assetsMaximumValue = canonical_NUMBER(); + auto const lossUnrealizedValue = canonical_NUMBER(); + auto const shareMPTIDValue = canonical_UINT192(); + auto const withdrawalPolicyValue = canonical_UINT8(); + auto const scaleValue = canonical_UINT8(); + + auto sle = std::make_shared(Vault::entryType, index); + + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfOwner) = ownerValue; + sle->at(sfAccount) = accountValue; + sle->at(sfData) = dataValue; + sle->at(sfAsset) = STIssue(sfAsset, assetValue); + sle->at(sfAssetsTotal) = assetsTotalValue; + sle->at(sfAssetsAvailable) = assetsAvailableValue; + sle->at(sfAssetsMaximum) = assetsMaximumValue; + sle->at(sfLossUnrealized) = lossUnrealizedValue; + sle->at(sfShareMPTID) = shareMPTIDValue; + sle->at(sfWithdrawalPolicy) = withdrawalPolicyValue; + sle->at(sfScale) = scaleValue; + + VaultBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Vault entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSle = entryFromSle.getSequence(); + auto const fromBuilder = entryFromBuilder.getSequence(); + + expectEqualField(expected, fromSle, "sfSequence"); + expectEqualField(expected, fromBuilder, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = ownerValue; + + auto const fromSle = entryFromSle.getOwner(); + auto const fromBuilder = entryFromBuilder.getOwner(); + + expectEqualField(expected, fromSle, "sfOwner"); + expectEqualField(expected, fromBuilder, "sfOwner"); + } + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = assetValue; + + auto const fromSle = entryFromSle.getAsset(); + auto const fromBuilder = entryFromBuilder.getAsset(); + + expectEqualField(expected, fromSle, "sfAsset"); + expectEqualField(expected, fromBuilder, "sfAsset"); + } + + { + auto const& expected = shareMPTIDValue; + + auto const fromSle = entryFromSle.getShareMPTID(); + auto const fromBuilder = entryFromBuilder.getShareMPTID(); + + expectEqualField(expected, fromSle, "sfShareMPTID"); + expectEqualField(expected, fromBuilder, "sfShareMPTID"); + } + + { + auto const& expected = withdrawalPolicyValue; + + auto const fromSle = entryFromSle.getWithdrawalPolicy(); + auto const fromBuilder = entryFromBuilder.getWithdrawalPolicy(); + + expectEqualField(expected, fromSle, "sfWithdrawalPolicy"); + expectEqualField(expected, fromBuilder, "sfWithdrawalPolicy"); + } + + { + auto const& expected = dataValue; + + auto const fromSleOpt = entryFromSle.getData(); + auto const fromBuilderOpt = entryFromBuilder.getData(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfData"); + expectEqualField(expected, *fromBuilderOpt, "sfData"); + } + + { + auto const& expected = assetsTotalValue; + + auto const fromSleOpt = entryFromSle.getAssetsTotal(); + auto const fromBuilderOpt = entryFromBuilder.getAssetsTotal(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAssetsTotal"); + expectEqualField(expected, *fromBuilderOpt, "sfAssetsTotal"); + } + + { + auto const& expected = assetsAvailableValue; + + auto const fromSleOpt = entryFromSle.getAssetsAvailable(); + auto const fromBuilderOpt = entryFromBuilder.getAssetsAvailable(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAssetsAvailable"); + expectEqualField(expected, *fromBuilderOpt, "sfAssetsAvailable"); + } + + { + auto const& expected = assetsMaximumValue; + + auto const fromSleOpt = entryFromSle.getAssetsMaximum(); + auto const fromBuilderOpt = entryFromBuilder.getAssetsMaximum(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAssetsMaximum"); + expectEqualField(expected, *fromBuilderOpt, "sfAssetsMaximum"); + } + + { + auto const& expected = lossUnrealizedValue; + + auto const fromSleOpt = entryFromSle.getLossUnrealized(); + auto const fromBuilderOpt = entryFromBuilder.getLossUnrealized(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLossUnrealized"); + expectEqualField(expected, *fromBuilderOpt, "sfLossUnrealized"); + } + + { + auto const& expected = scaleValue; + + auto const fromSleOpt = entryFromSle.getScale(); + auto const fromBuilderOpt = entryFromBuilder.getScale(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfScale"); + expectEqualField(expected, *fromBuilderOpt, "sfScale"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(VaultTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Vault{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(VaultTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(VaultBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(VaultTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const ownerValue = canonical_ACCOUNT(); + auto const accountValue = canonical_ACCOUNT(); + auto const assetValue = canonical_ISSUE(); + auto const shareMPTIDValue = canonical_UINT192(); + auto const withdrawalPolicyValue = canonical_UINT8(); + + VaultBuilder builder{ + previousTxnIDValue, + previousTxnLgrSeqValue, + sequenceValue, + ownerNodeValue, + ownerValue, + accountValue, + assetValue, + shareMPTIDValue, + withdrawalPolicyValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasData()); + EXPECT_FALSE(entry.getData().has_value()); + EXPECT_FALSE(entry.hasAssetsTotal()); + EXPECT_FALSE(entry.getAssetsTotal().has_value()); + EXPECT_FALSE(entry.hasAssetsAvailable()); + EXPECT_FALSE(entry.getAssetsAvailable().has_value()); + EXPECT_FALSE(entry.hasAssetsMaximum()); + EXPECT_FALSE(entry.getAssetsMaximum().has_value()); + EXPECT_FALSE(entry.hasLossUnrealized()); + EXPECT_FALSE(entry.getLossUnrealized().has_value()); + EXPECT_FALSE(entry.hasScale()); + EXPECT_FALSE(entry.getScale().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedClaimIDTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedClaimIDTests.cpp new file mode 100644 index 0000000000..4b6979b86a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedClaimIDTests.cpp @@ -0,0 +1,283 @@ +// Auto-generated unit tests for ledger entry XChainOwnedClaimID + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(XChainOwnedClaimIDTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + auto const xChainClaimAttestationsValue = canonical_ARRAY(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + XChainOwnedClaimIDBuilder builder{ + accountValue, + xChainBridgeValue, + xChainClaimIDValue, + otherChainSourceValue, + xChainClaimAttestationsValue, + signatureRewardValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = xChainBridgeValue; + auto const actual = entry.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = entry.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + { + auto const& expected = otherChainSourceValue; + auto const actual = entry.getOtherChainSource(); + expectEqualField(expected, actual, "sfOtherChainSource"); + } + + { + auto const& expected = xChainClaimAttestationsValue; + auto const actual = entry.getXChainClaimAttestations(); + expectEqualField(expected, actual, "sfXChainClaimAttestations"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = entry.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(XChainOwnedClaimIDTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + auto const xChainClaimAttestationsValue = canonical_ARRAY(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(XChainOwnedClaimID::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfXChainBridge) = xChainBridgeValue; + sle->at(sfXChainClaimID) = xChainClaimIDValue; + sle->at(sfOtherChainSource) = otherChainSourceValue; + sle->setFieldArray(sfXChainClaimAttestations, xChainClaimAttestationsValue); + sle->at(sfSignatureReward) = signatureRewardValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + XChainOwnedClaimIDBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + XChainOwnedClaimID entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = xChainBridgeValue; + + auto const fromSle = entryFromSle.getXChainBridge(); + auto const fromBuilder = entryFromBuilder.getXChainBridge(); + + expectEqualField(expected, fromSle, "sfXChainBridge"); + expectEqualField(expected, fromBuilder, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + + auto const fromSle = entryFromSle.getXChainClaimID(); + auto const fromBuilder = entryFromBuilder.getXChainClaimID(); + + expectEqualField(expected, fromSle, "sfXChainClaimID"); + expectEqualField(expected, fromBuilder, "sfXChainClaimID"); + } + + { + auto const& expected = otherChainSourceValue; + + auto const fromSle = entryFromSle.getOtherChainSource(); + auto const fromBuilder = entryFromBuilder.getOtherChainSource(); + + expectEqualField(expected, fromSle, "sfOtherChainSource"); + expectEqualField(expected, fromBuilder, "sfOtherChainSource"); + } + + { + auto const& expected = xChainClaimAttestationsValue; + + auto const fromSle = entryFromSle.getXChainClaimAttestations(); + auto const fromBuilder = entryFromBuilder.getXChainClaimAttestations(); + + expectEqualField(expected, fromSle, "sfXChainClaimAttestations"); + expectEqualField(expected, fromBuilder, "sfXChainClaimAttestations"); + } + + { + auto const& expected = signatureRewardValue; + + auto const fromSle = entryFromSle.getSignatureReward(); + auto const fromBuilder = entryFromBuilder.getSignatureReward(); + + expectEqualField(expected, fromSle, "sfSignatureReward"); + expectEqualField(expected, fromBuilder, "sfSignatureReward"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(XChainOwnedClaimIDTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(XChainOwnedClaimID{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(XChainOwnedClaimIDTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(XChainOwnedClaimIDBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimIDTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimIDTests.cpp new file mode 100644 index 0000000000..0e4e1fb977 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimIDTests.cpp @@ -0,0 +1,243 @@ +// Auto-generated unit tests for ledger entry XChainOwnedCreateAccountClaimID + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(XChainOwnedCreateAccountClaimIDTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainAccountCreateCountValue = canonical_UINT64(); + auto const xChainCreateAccountAttestationsValue = canonical_ARRAY(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + XChainOwnedCreateAccountClaimIDBuilder builder{ + accountValue, + xChainBridgeValue, + xChainAccountCreateCountValue, + xChainCreateAccountAttestationsValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = xChainBridgeValue; + auto const actual = entry.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = xChainAccountCreateCountValue; + auto const actual = entry.getXChainAccountCreateCount(); + expectEqualField(expected, actual, "sfXChainAccountCreateCount"); + } + + { + auto const& expected = xChainCreateAccountAttestationsValue; + auto const actual = entry.getXChainCreateAccountAttestations(); + expectEqualField(expected, actual, "sfXChainCreateAccountAttestations"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(XChainOwnedCreateAccountClaimIDTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainAccountCreateCountValue = canonical_UINT64(); + auto const xChainCreateAccountAttestationsValue = canonical_ARRAY(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(XChainOwnedCreateAccountClaimID::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfXChainBridge) = xChainBridgeValue; + sle->at(sfXChainAccountCreateCount) = xChainAccountCreateCountValue; + sle->setFieldArray(sfXChainCreateAccountAttestations, xChainCreateAccountAttestationsValue); + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + XChainOwnedCreateAccountClaimIDBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + XChainOwnedCreateAccountClaimID entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = xChainBridgeValue; + + auto const fromSle = entryFromSle.getXChainBridge(); + auto const fromBuilder = entryFromBuilder.getXChainBridge(); + + expectEqualField(expected, fromSle, "sfXChainBridge"); + expectEqualField(expected, fromBuilder, "sfXChainBridge"); + } + + { + auto const& expected = xChainAccountCreateCountValue; + + auto const fromSle = entryFromSle.getXChainAccountCreateCount(); + auto const fromBuilder = entryFromBuilder.getXChainAccountCreateCount(); + + expectEqualField(expected, fromSle, "sfXChainAccountCreateCount"); + expectEqualField(expected, fromBuilder, "sfXChainAccountCreateCount"); + } + + { + auto const& expected = xChainCreateAccountAttestationsValue; + + auto const fromSle = entryFromSle.getXChainCreateAccountAttestations(); + auto const fromBuilder = entryFromBuilder.getXChainCreateAccountAttestations(); + + expectEqualField(expected, fromSle, "sfXChainCreateAccountAttestations"); + expectEqualField(expected, fromBuilder, "sfXChainCreateAccountAttestations"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(XChainOwnedCreateAccountClaimIDTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(XChainOwnedCreateAccountClaimID{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(XChainOwnedCreateAccountClaimIDTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(XChainOwnedCreateAccountClaimIDBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AMMBidTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AMMBidTests.cpp new file mode 100644 index 0000000000..c9db0cabcf --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AMMBidTests.cpp @@ -0,0 +1,255 @@ +// Auto-generated unit tests for transaction AMMBid + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAMMBidTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMBid")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const bidMinValue = canonical_AMOUNT(); + auto const bidMaxValue = canonical_AMOUNT(); + auto const authAccountsValue = canonical_ARRAY(); + + AMMBidBuilder builder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setBidMin(bidMinValue); + builder.setBidMax(bidMaxValue); + builder.setAuthAccounts(authAccountsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = tx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = tx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = bidMinValue; + auto const actualOpt = tx.getBidMin(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBidMin should be present"; + expectEqualField(expected, *actualOpt, "sfBidMin"); + EXPECT_TRUE(tx.hasBidMin()); + } + + { + auto const& expected = bidMaxValue; + auto const actualOpt = tx.getBidMax(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBidMax should be present"; + expectEqualField(expected, *actualOpt, "sfBidMax"); + EXPECT_TRUE(tx.hasBidMax()); + } + + { + auto const& expected = authAccountsValue; + auto const actualOpt = tx.getAuthAccounts(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAuthAccounts should be present"; + expectEqualField(expected, *actualOpt, "sfAuthAccounts"); + EXPECT_TRUE(tx.hasAuthAccounts()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAMMBidTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMBidFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const bidMinValue = canonical_AMOUNT(); + auto const bidMaxValue = canonical_AMOUNT(); + auto const authAccountsValue = canonical_ARRAY(); + + // Build an initial transaction + AMMBidBuilder initialBuilder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + initialBuilder.setBidMin(bidMinValue); + initialBuilder.setBidMax(bidMaxValue); + initialBuilder.setAuthAccounts(authAccountsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AMMBidBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = rebuiltTx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = rebuiltTx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = bidMinValue; + auto const actualOpt = rebuiltTx.getBidMin(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBidMin should be present"; + expectEqualField(expected, *actualOpt, "sfBidMin"); + } + + { + auto const& expected = bidMaxValue; + auto const actualOpt = rebuiltTx.getBidMax(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBidMax should be present"; + expectEqualField(expected, *actualOpt, "sfBidMax"); + } + + { + auto const& expected = authAccountsValue; + auto const actualOpt = rebuiltTx.getAuthAccounts(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAuthAccounts should be present"; + expectEqualField(expected, *actualOpt, "sfAuthAccounts"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAMMBidTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMBid{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAMMBidTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMBidBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsAMMBidTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMBidNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + + AMMBidBuilder builder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasBidMin()); + EXPECT_FALSE(tx.getBidMin().has_value()); + EXPECT_FALSE(tx.hasBidMax()); + EXPECT_FALSE(tx.getBidMax().has_value()); + EXPECT_FALSE(tx.hasAuthAccounts()); + EXPECT_FALSE(tx.getAuthAccounts().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AMMClawbackTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AMMClawbackTests.cpp new file mode 100644 index 0000000000..d01e840ce5 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AMMClawbackTests.cpp @@ -0,0 +1,231 @@ +// Auto-generated unit tests for transaction AMMClawback + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAMMClawbackTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMClawback")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const holderValue = canonical_ACCOUNT(); + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const amountValue = canonical_AMOUNT(); + + AMMClawbackBuilder builder{ + accountValue, + holderValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAmount(amountValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = holderValue; + auto const actual = tx.getHolder(); + expectEqualField(expected, actual, "sfHolder"); + } + + { + auto const& expected = assetValue; + auto const actual = tx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = tx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAMMClawbackTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMClawbackFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const holderValue = canonical_ACCOUNT(); + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const amountValue = canonical_AMOUNT(); + + // Build an initial transaction + AMMClawbackBuilder initialBuilder{ + accountValue, + holderValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + initialBuilder.setAmount(amountValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AMMClawbackBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = holderValue; + auto const actual = rebuiltTx.getHolder(); + expectEqualField(expected, actual, "sfHolder"); + } + + { + auto const& expected = assetValue; + auto const actual = rebuiltTx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = rebuiltTx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAMMClawbackTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMClawback{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAMMClawbackTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMClawbackBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsAMMClawbackTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMClawbackNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const holderValue = canonical_ACCOUNT(); + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + + AMMClawbackBuilder builder{ + accountValue, + holderValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AMMCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AMMCreateTests.cpp new file mode 100644 index 0000000000..b55c9f377e --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AMMCreateTests.cpp @@ -0,0 +1,178 @@ +// Auto-generated unit tests for transaction AMMCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAMMCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const amountValue = canonical_AMOUNT(); + auto const amount2Value = canonical_AMOUNT(); + auto const tradingFeeValue = canonical_UINT16(); + + AMMCreateBuilder builder{ + accountValue, + amountValue, + amount2Value, + tradingFeeValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = amount2Value; + auto const actual = tx.getAmount2(); + expectEqualField(expected, actual, "sfAmount2"); + } + + { + auto const& expected = tradingFeeValue; + auto const actual = tx.getTradingFee(); + expectEqualField(expected, actual, "sfTradingFee"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAMMCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const amountValue = canonical_AMOUNT(); + auto const amount2Value = canonical_AMOUNT(); + auto const tradingFeeValue = canonical_UINT16(); + + // Build an initial transaction + AMMCreateBuilder initialBuilder{ + accountValue, + amountValue, + amount2Value, + tradingFeeValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AMMCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = amount2Value; + auto const actual = rebuiltTx.getAmount2(); + expectEqualField(expected, actual, "sfAmount2"); + } + + { + auto const& expected = tradingFeeValue; + auto const actual = rebuiltTx.getTradingFee(); + expectEqualField(expected, actual, "sfTradingFee"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAMMCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAMMCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AMMDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AMMDeleteTests.cpp new file mode 100644 index 0000000000..774d993a1a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AMMDeleteTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction AMMDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAMMDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + + AMMDeleteBuilder builder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = tx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = tx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAMMDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + + // Build an initial transaction + AMMDeleteBuilder initialBuilder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AMMDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = rebuiltTx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = rebuiltTx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAMMDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAMMDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AMMDepositTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AMMDepositTests.cpp new file mode 100644 index 0000000000..9917848f0a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AMMDepositTests.cpp @@ -0,0 +1,297 @@ +// Auto-generated unit tests for transaction AMMDeposit + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAMMDepositTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMDeposit")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const amountValue = canonical_AMOUNT(); + auto const amount2Value = canonical_AMOUNT(); + auto const ePriceValue = canonical_AMOUNT(); + auto const lPTokenOutValue = canonical_AMOUNT(); + auto const tradingFeeValue = canonical_UINT16(); + + AMMDepositBuilder builder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAmount(amountValue); + builder.setAmount2(amount2Value); + builder.setEPrice(ePriceValue); + builder.setLPTokenOut(lPTokenOutValue); + builder.setTradingFee(tradingFeeValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = tx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = tx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + + { + auto const& expected = amount2Value; + auto const actualOpt = tx.getAmount2(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount2 should be present"; + expectEqualField(expected, *actualOpt, "sfAmount2"); + EXPECT_TRUE(tx.hasAmount2()); + } + + { + auto const& expected = ePriceValue; + auto const actualOpt = tx.getEPrice(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfEPrice should be present"; + expectEqualField(expected, *actualOpt, "sfEPrice"); + EXPECT_TRUE(tx.hasEPrice()); + } + + { + auto const& expected = lPTokenOutValue; + auto const actualOpt = tx.getLPTokenOut(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLPTokenOut should be present"; + expectEqualField(expected, *actualOpt, "sfLPTokenOut"); + EXPECT_TRUE(tx.hasLPTokenOut()); + } + + { + auto const& expected = tradingFeeValue; + auto const actualOpt = tx.getTradingFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTradingFee should be present"; + expectEqualField(expected, *actualOpt, "sfTradingFee"); + EXPECT_TRUE(tx.hasTradingFee()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAMMDepositTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMDepositFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const amountValue = canonical_AMOUNT(); + auto const amount2Value = canonical_AMOUNT(); + auto const ePriceValue = canonical_AMOUNT(); + auto const lPTokenOutValue = canonical_AMOUNT(); + auto const tradingFeeValue = canonical_UINT16(); + + // Build an initial transaction + AMMDepositBuilder initialBuilder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + initialBuilder.setAmount(amountValue); + initialBuilder.setAmount2(amount2Value); + initialBuilder.setEPrice(ePriceValue); + initialBuilder.setLPTokenOut(lPTokenOutValue); + initialBuilder.setTradingFee(tradingFeeValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AMMDepositBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = rebuiltTx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = rebuiltTx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + + { + auto const& expected = amount2Value; + auto const actualOpt = rebuiltTx.getAmount2(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount2 should be present"; + expectEqualField(expected, *actualOpt, "sfAmount2"); + } + + { + auto const& expected = ePriceValue; + auto const actualOpt = rebuiltTx.getEPrice(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfEPrice should be present"; + expectEqualField(expected, *actualOpt, "sfEPrice"); + } + + { + auto const& expected = lPTokenOutValue; + auto const actualOpt = rebuiltTx.getLPTokenOut(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLPTokenOut should be present"; + expectEqualField(expected, *actualOpt, "sfLPTokenOut"); + } + + { + auto const& expected = tradingFeeValue; + auto const actualOpt = rebuiltTx.getTradingFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTradingFee should be present"; + expectEqualField(expected, *actualOpt, "sfTradingFee"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAMMDepositTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMDeposit{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAMMDepositTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMDepositBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsAMMDepositTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMDepositNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + + AMMDepositBuilder builder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); + EXPECT_FALSE(tx.hasAmount2()); + EXPECT_FALSE(tx.getAmount2().has_value()); + EXPECT_FALSE(tx.hasEPrice()); + EXPECT_FALSE(tx.getEPrice().has_value()); + EXPECT_FALSE(tx.hasLPTokenOut()); + EXPECT_FALSE(tx.getLPTokenOut().has_value()); + EXPECT_FALSE(tx.hasTradingFee()); + EXPECT_FALSE(tx.getTradingFee().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AMMVoteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AMMVoteTests.cpp new file mode 100644 index 0000000000..6a47b7613e --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AMMVoteTests.cpp @@ -0,0 +1,178 @@ +// Auto-generated unit tests for transaction AMMVote + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAMMVoteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMVote")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const tradingFeeValue = canonical_UINT16(); + + AMMVoteBuilder builder{ + accountValue, + assetValue, + asset2Value, + tradingFeeValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = tx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = tx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + { + auto const& expected = tradingFeeValue; + auto const actual = tx.getTradingFee(); + expectEqualField(expected, actual, "sfTradingFee"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAMMVoteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMVoteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const tradingFeeValue = canonical_UINT16(); + + // Build an initial transaction + AMMVoteBuilder initialBuilder{ + accountValue, + assetValue, + asset2Value, + tradingFeeValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AMMVoteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = rebuiltTx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = rebuiltTx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + { + auto const& expected = tradingFeeValue; + auto const actual = rebuiltTx.getTradingFee(); + expectEqualField(expected, actual, "sfTradingFee"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAMMVoteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMVote{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAMMVoteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMVoteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AMMWithdrawTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AMMWithdrawTests.cpp new file mode 100644 index 0000000000..0fd529c282 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AMMWithdrawTests.cpp @@ -0,0 +1,276 @@ +// Auto-generated unit tests for transaction AMMWithdraw + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAMMWithdrawTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMWithdraw")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const amountValue = canonical_AMOUNT(); + auto const amount2Value = canonical_AMOUNT(); + auto const ePriceValue = canonical_AMOUNT(); + auto const lPTokenInValue = canonical_AMOUNT(); + + AMMWithdrawBuilder builder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAmount(amountValue); + builder.setAmount2(amount2Value); + builder.setEPrice(ePriceValue); + builder.setLPTokenIn(lPTokenInValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = tx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = tx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + + { + auto const& expected = amount2Value; + auto const actualOpt = tx.getAmount2(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount2 should be present"; + expectEqualField(expected, *actualOpt, "sfAmount2"); + EXPECT_TRUE(tx.hasAmount2()); + } + + { + auto const& expected = ePriceValue; + auto const actualOpt = tx.getEPrice(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfEPrice should be present"; + expectEqualField(expected, *actualOpt, "sfEPrice"); + EXPECT_TRUE(tx.hasEPrice()); + } + + { + auto const& expected = lPTokenInValue; + auto const actualOpt = tx.getLPTokenIn(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLPTokenIn should be present"; + expectEqualField(expected, *actualOpt, "sfLPTokenIn"); + EXPECT_TRUE(tx.hasLPTokenIn()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAMMWithdrawTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMWithdrawFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const amountValue = canonical_AMOUNT(); + auto const amount2Value = canonical_AMOUNT(); + auto const ePriceValue = canonical_AMOUNT(); + auto const lPTokenInValue = canonical_AMOUNT(); + + // Build an initial transaction + AMMWithdrawBuilder initialBuilder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + initialBuilder.setAmount(amountValue); + initialBuilder.setAmount2(amount2Value); + initialBuilder.setEPrice(ePriceValue); + initialBuilder.setLPTokenIn(lPTokenInValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AMMWithdrawBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = rebuiltTx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = rebuiltTx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + + { + auto const& expected = amount2Value; + auto const actualOpt = rebuiltTx.getAmount2(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount2 should be present"; + expectEqualField(expected, *actualOpt, "sfAmount2"); + } + + { + auto const& expected = ePriceValue; + auto const actualOpt = rebuiltTx.getEPrice(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfEPrice should be present"; + expectEqualField(expected, *actualOpt, "sfEPrice"); + } + + { + auto const& expected = lPTokenInValue; + auto const actualOpt = rebuiltTx.getLPTokenIn(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLPTokenIn should be present"; + expectEqualField(expected, *actualOpt, "sfLPTokenIn"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAMMWithdrawTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMWithdraw{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAMMWithdrawTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMWithdrawBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsAMMWithdrawTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMWithdrawNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + + AMMWithdrawBuilder builder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); + EXPECT_FALSE(tx.hasAmount2()); + EXPECT_FALSE(tx.getAmount2().has_value()); + EXPECT_FALSE(tx.hasEPrice()); + EXPECT_FALSE(tx.getEPrice().has_value()); + EXPECT_FALSE(tx.hasLPTokenIn()); + EXPECT_FALSE(tx.getLPTokenIn().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AccountDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AccountDeleteTests.cpp new file mode 100644 index 0000000000..0fb951221b --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AccountDeleteTests.cpp @@ -0,0 +1,216 @@ +// Auto-generated unit tests for transaction AccountDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAccountDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAccountDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + auto const credentialIDsValue = canonical_VECTOR256(); + + AccountDeleteBuilder builder{ + accountValue, + destinationValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDestinationTag(destinationTagValue); + builder.setCredentialIDs(credentialIDsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + // Verify optional fields + { + auto const& expected = destinationTagValue; + auto const actualOpt = tx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + EXPECT_TRUE(tx.hasDestinationTag()); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = tx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + EXPECT_TRUE(tx.hasCredentialIDs()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAccountDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAccountDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + auto const credentialIDsValue = canonical_VECTOR256(); + + // Build an initial transaction + AccountDeleteBuilder initialBuilder{ + accountValue, + destinationValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDestinationTag(destinationTagValue); + initialBuilder.setCredentialIDs(credentialIDsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AccountDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + // Verify optional fields + { + auto const& expected = destinationTagValue; + auto const actualOpt = rebuiltTx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = rebuiltTx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAccountDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AccountDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAccountDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AccountDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsAccountDeleteTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAccountDeleteNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const destinationValue = canonical_ACCOUNT(); + + AccountDeleteBuilder builder{ + accountValue, + destinationValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasDestinationTag()); + EXPECT_FALSE(tx.getDestinationTag().has_value()); + EXPECT_FALSE(tx.hasCredentialIDs()); + EXPECT_FALSE(tx.getCredentialIDs().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AccountSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AccountSetTests.cpp new file mode 100644 index 0000000000..993fe55742 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AccountSetTests.cpp @@ -0,0 +1,366 @@ +// Auto-generated unit tests for transaction AccountSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAccountSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAccountSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const emailHashValue = canonical_UINT128(); + auto const walletLocatorValue = canonical_UINT256(); + auto const walletSizeValue = canonical_UINT32(); + auto const messageKeyValue = canonical_VL(); + auto const domainValue = canonical_VL(); + auto const transferRateValue = canonical_UINT32(); + auto const setFlagValue = canonical_UINT32(); + auto const clearFlagValue = canonical_UINT32(); + auto const tickSizeValue = canonical_UINT8(); + auto const nFTokenMinterValue = canonical_ACCOUNT(); + + AccountSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setEmailHash(emailHashValue); + builder.setWalletLocator(walletLocatorValue); + builder.setWalletSize(walletSizeValue); + builder.setMessageKey(messageKeyValue); + builder.setDomain(domainValue); + builder.setTransferRate(transferRateValue); + builder.setSetFlag(setFlagValue); + builder.setClearFlag(clearFlagValue); + builder.setTickSize(tickSizeValue); + builder.setNFTokenMinter(nFTokenMinterValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = emailHashValue; + auto const actualOpt = tx.getEmailHash(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfEmailHash should be present"; + expectEqualField(expected, *actualOpt, "sfEmailHash"); + EXPECT_TRUE(tx.hasEmailHash()); + } + + { + auto const& expected = walletLocatorValue; + auto const actualOpt = tx.getWalletLocator(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfWalletLocator should be present"; + expectEqualField(expected, *actualOpt, "sfWalletLocator"); + EXPECT_TRUE(tx.hasWalletLocator()); + } + + { + auto const& expected = walletSizeValue; + auto const actualOpt = tx.getWalletSize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfWalletSize should be present"; + expectEqualField(expected, *actualOpt, "sfWalletSize"); + EXPECT_TRUE(tx.hasWalletSize()); + } + + { + auto const& expected = messageKeyValue; + auto const actualOpt = tx.getMessageKey(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMessageKey should be present"; + expectEqualField(expected, *actualOpt, "sfMessageKey"); + EXPECT_TRUE(tx.hasMessageKey()); + } + + { + auto const& expected = domainValue; + auto const actualOpt = tx.getDomain(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomain should be present"; + expectEqualField(expected, *actualOpt, "sfDomain"); + EXPECT_TRUE(tx.hasDomain()); + } + + { + auto const& expected = transferRateValue; + auto const actualOpt = tx.getTransferRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferRate should be present"; + expectEqualField(expected, *actualOpt, "sfTransferRate"); + EXPECT_TRUE(tx.hasTransferRate()); + } + + { + auto const& expected = setFlagValue; + auto const actualOpt = tx.getSetFlag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSetFlag should be present"; + expectEqualField(expected, *actualOpt, "sfSetFlag"); + EXPECT_TRUE(tx.hasSetFlag()); + } + + { + auto const& expected = clearFlagValue; + auto const actualOpt = tx.getClearFlag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfClearFlag should be present"; + expectEqualField(expected, *actualOpt, "sfClearFlag"); + EXPECT_TRUE(tx.hasClearFlag()); + } + + { + auto const& expected = tickSizeValue; + auto const actualOpt = tx.getTickSize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTickSize should be present"; + expectEqualField(expected, *actualOpt, "sfTickSize"); + EXPECT_TRUE(tx.hasTickSize()); + } + + { + auto const& expected = nFTokenMinterValue; + auto const actualOpt = tx.getNFTokenMinter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenMinter should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenMinter"); + EXPECT_TRUE(tx.hasNFTokenMinter()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAccountSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAccountSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const emailHashValue = canonical_UINT128(); + auto const walletLocatorValue = canonical_UINT256(); + auto const walletSizeValue = canonical_UINT32(); + auto const messageKeyValue = canonical_VL(); + auto const domainValue = canonical_VL(); + auto const transferRateValue = canonical_UINT32(); + auto const setFlagValue = canonical_UINT32(); + auto const clearFlagValue = canonical_UINT32(); + auto const tickSizeValue = canonical_UINT8(); + auto const nFTokenMinterValue = canonical_ACCOUNT(); + + // Build an initial transaction + AccountSetBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setEmailHash(emailHashValue); + initialBuilder.setWalletLocator(walletLocatorValue); + initialBuilder.setWalletSize(walletSizeValue); + initialBuilder.setMessageKey(messageKeyValue); + initialBuilder.setDomain(domainValue); + initialBuilder.setTransferRate(transferRateValue); + initialBuilder.setSetFlag(setFlagValue); + initialBuilder.setClearFlag(clearFlagValue); + initialBuilder.setTickSize(tickSizeValue); + initialBuilder.setNFTokenMinter(nFTokenMinterValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AccountSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = emailHashValue; + auto const actualOpt = rebuiltTx.getEmailHash(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfEmailHash should be present"; + expectEqualField(expected, *actualOpt, "sfEmailHash"); + } + + { + auto const& expected = walletLocatorValue; + auto const actualOpt = rebuiltTx.getWalletLocator(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfWalletLocator should be present"; + expectEqualField(expected, *actualOpt, "sfWalletLocator"); + } + + { + auto const& expected = walletSizeValue; + auto const actualOpt = rebuiltTx.getWalletSize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfWalletSize should be present"; + expectEqualField(expected, *actualOpt, "sfWalletSize"); + } + + { + auto const& expected = messageKeyValue; + auto const actualOpt = rebuiltTx.getMessageKey(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMessageKey should be present"; + expectEqualField(expected, *actualOpt, "sfMessageKey"); + } + + { + auto const& expected = domainValue; + auto const actualOpt = rebuiltTx.getDomain(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomain should be present"; + expectEqualField(expected, *actualOpt, "sfDomain"); + } + + { + auto const& expected = transferRateValue; + auto const actualOpt = rebuiltTx.getTransferRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferRate should be present"; + expectEqualField(expected, *actualOpt, "sfTransferRate"); + } + + { + auto const& expected = setFlagValue; + auto const actualOpt = rebuiltTx.getSetFlag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSetFlag should be present"; + expectEqualField(expected, *actualOpt, "sfSetFlag"); + } + + { + auto const& expected = clearFlagValue; + auto const actualOpt = rebuiltTx.getClearFlag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfClearFlag should be present"; + expectEqualField(expected, *actualOpt, "sfClearFlag"); + } + + { + auto const& expected = tickSizeValue; + auto const actualOpt = rebuiltTx.getTickSize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTickSize should be present"; + expectEqualField(expected, *actualOpt, "sfTickSize"); + } + + { + auto const& expected = nFTokenMinterValue; + auto const actualOpt = rebuiltTx.getNFTokenMinter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenMinter should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenMinter"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAccountSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + OfferCancelBuilder wrongBuilder{account, canonical_UINT32(), 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AccountSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAccountSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + OfferCancelBuilder wrongBuilder{account, canonical_UINT32(), 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AccountSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsAccountSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAccountSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + AccountSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasEmailHash()); + EXPECT_FALSE(tx.getEmailHash().has_value()); + EXPECT_FALSE(tx.hasWalletLocator()); + EXPECT_FALSE(tx.getWalletLocator().has_value()); + EXPECT_FALSE(tx.hasWalletSize()); + EXPECT_FALSE(tx.getWalletSize().has_value()); + EXPECT_FALSE(tx.hasMessageKey()); + EXPECT_FALSE(tx.getMessageKey().has_value()); + EXPECT_FALSE(tx.hasDomain()); + EXPECT_FALSE(tx.getDomain().has_value()); + EXPECT_FALSE(tx.hasTransferRate()); + EXPECT_FALSE(tx.getTransferRate().has_value()); + EXPECT_FALSE(tx.hasSetFlag()); + EXPECT_FALSE(tx.getSetFlag().has_value()); + EXPECT_FALSE(tx.hasClearFlag()); + EXPECT_FALSE(tx.getClearFlag().has_value()); + EXPECT_FALSE(tx.hasTickSize()); + EXPECT_FALSE(tx.getTickSize().has_value()); + EXPECT_FALSE(tx.hasNFTokenMinter()); + EXPECT_FALSE(tx.getNFTokenMinter().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/BatchTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/BatchTests.cpp new file mode 100644 index 0000000000..c6818a26d7 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/BatchTests.cpp @@ -0,0 +1,195 @@ +// Auto-generated unit tests for transaction Batch + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsBatchTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testBatch")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const rawTransactionsValue = canonical_ARRAY(); + auto const batchSignersValue = canonical_ARRAY(); + + BatchBuilder builder{ + accountValue, + rawTransactionsValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setBatchSigners(batchSignersValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = rawTransactionsValue; + auto const actual = tx.getRawTransactions(); + expectEqualField(expected, actual, "sfRawTransactions"); + } + + // Verify optional fields + { + auto const& expected = batchSignersValue; + auto const actualOpt = tx.getBatchSigners(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBatchSigners should be present"; + expectEqualField(expected, *actualOpt, "sfBatchSigners"); + EXPECT_TRUE(tx.hasBatchSigners()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsBatchTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testBatchFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const rawTransactionsValue = canonical_ARRAY(); + auto const batchSignersValue = canonical_ARRAY(); + + // Build an initial transaction + BatchBuilder initialBuilder{ + accountValue, + rawTransactionsValue, + sequenceValue, + feeValue + }; + + initialBuilder.setBatchSigners(batchSignersValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + BatchBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = rawTransactionsValue; + auto const actual = rebuiltTx.getRawTransactions(); + expectEqualField(expected, actual, "sfRawTransactions"); + } + + // Verify optional fields + { + auto const& expected = batchSignersValue; + auto const actualOpt = rebuiltTx.getBatchSigners(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBatchSigners should be present"; + expectEqualField(expected, *actualOpt, "sfBatchSigners"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsBatchTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(Batch{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsBatchTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(BatchBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsBatchTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testBatchNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const rawTransactionsValue = canonical_ARRAY(); + + BatchBuilder builder{ + accountValue, + rawTransactionsValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasBatchSigners()); + EXPECT_FALSE(tx.getBatchSigners().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/CheckCancelTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/CheckCancelTests.cpp new file mode 100644 index 0000000000..afdc2d6d66 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/CheckCancelTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction CheckCancel + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsCheckCancelTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCancel")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const checkIDValue = canonical_UINT256(); + + CheckCancelBuilder builder{ + accountValue, + checkIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = checkIDValue; + auto const actual = tx.getCheckID(); + expectEqualField(expected, actual, "sfCheckID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsCheckCancelTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCancelFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const checkIDValue = canonical_UINT256(); + + // Build an initial transaction + CheckCancelBuilder initialBuilder{ + accountValue, + checkIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + CheckCancelBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = checkIDValue; + auto const actual = rebuiltTx.getCheckID(); + expectEqualField(expected, actual, "sfCheckID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsCheckCancelTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CheckCancel{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsCheckCancelTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CheckCancelBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/CheckCashTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/CheckCashTests.cpp new file mode 100644 index 0000000000..b8c4dccd55 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/CheckCashTests.cpp @@ -0,0 +1,216 @@ +// Auto-generated unit tests for transaction CheckCash + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsCheckCashTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCash")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const checkIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const deliverMinValue = canonical_AMOUNT(); + + CheckCashBuilder builder{ + accountValue, + checkIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAmount(amountValue); + builder.setDeliverMin(deliverMinValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = checkIDValue; + auto const actual = tx.getCheckID(); + expectEqualField(expected, actual, "sfCheckID"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + + { + auto const& expected = deliverMinValue; + auto const actualOpt = tx.getDeliverMin(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDeliverMin should be present"; + expectEqualField(expected, *actualOpt, "sfDeliverMin"); + EXPECT_TRUE(tx.hasDeliverMin()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsCheckCashTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCashFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const checkIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const deliverMinValue = canonical_AMOUNT(); + + // Build an initial transaction + CheckCashBuilder initialBuilder{ + accountValue, + checkIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setAmount(amountValue); + initialBuilder.setDeliverMin(deliverMinValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + CheckCashBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = checkIDValue; + auto const actual = rebuiltTx.getCheckID(); + expectEqualField(expected, actual, "sfCheckID"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + + { + auto const& expected = deliverMinValue; + auto const actualOpt = rebuiltTx.getDeliverMin(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDeliverMin should be present"; + expectEqualField(expected, *actualOpt, "sfDeliverMin"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsCheckCashTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CheckCash{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsCheckCashTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CheckCashBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsCheckCashTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCashNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const checkIDValue = canonical_UINT256(); + + CheckCashBuilder builder{ + accountValue, + checkIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); + EXPECT_FALSE(tx.hasDeliverMin()); + EXPECT_FALSE(tx.getDeliverMin().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/CheckCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/CheckCreateTests.cpp new file mode 100644 index 0000000000..9b8a831913 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/CheckCreateTests.cpp @@ -0,0 +1,255 @@ +// Auto-generated unit tests for transaction CheckCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsCheckCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + auto const expirationValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const invoiceIDValue = canonical_UINT256(); + + CheckCreateBuilder builder{ + accountValue, + destinationValue, + sendMaxValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setExpiration(expirationValue); + builder.setDestinationTag(destinationTagValue); + builder.setInvoiceID(invoiceIDValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = sendMaxValue; + auto const actual = tx.getSendMax(); + expectEqualField(expected, actual, "sfSendMax"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = tx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(tx.hasExpiration()); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = tx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + EXPECT_TRUE(tx.hasDestinationTag()); + } + + { + auto const& expected = invoiceIDValue; + auto const actualOpt = tx.getInvoiceID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInvoiceID should be present"; + expectEqualField(expected, *actualOpt, "sfInvoiceID"); + EXPECT_TRUE(tx.hasInvoiceID()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsCheckCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + auto const expirationValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const invoiceIDValue = canonical_UINT256(); + + // Build an initial transaction + CheckCreateBuilder initialBuilder{ + accountValue, + destinationValue, + sendMaxValue, + sequenceValue, + feeValue + }; + + initialBuilder.setExpiration(expirationValue); + initialBuilder.setDestinationTag(destinationTagValue); + initialBuilder.setInvoiceID(invoiceIDValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + CheckCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = sendMaxValue; + auto const actual = rebuiltTx.getSendMax(); + expectEqualField(expected, actual, "sfSendMax"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = rebuiltTx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = rebuiltTx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + } + + { + auto const& expected = invoiceIDValue; + auto const actualOpt = rebuiltTx.getInvoiceID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInvoiceID should be present"; + expectEqualField(expected, *actualOpt, "sfInvoiceID"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsCheckCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CheckCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsCheckCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CheckCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsCheckCreateTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCreateNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const destinationValue = canonical_ACCOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + + CheckCreateBuilder builder{ + accountValue, + destinationValue, + sendMaxValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasExpiration()); + EXPECT_FALSE(tx.getExpiration().has_value()); + EXPECT_FALSE(tx.hasDestinationTag()); + EXPECT_FALSE(tx.getDestinationTag().has_value()); + EXPECT_FALSE(tx.hasInvoiceID()); + EXPECT_FALSE(tx.getInvoiceID().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/ClawbackTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/ClawbackTests.cpp new file mode 100644 index 0000000000..2f9b7f1e3a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/ClawbackTests.cpp @@ -0,0 +1,195 @@ +// Auto-generated unit tests for transaction Clawback + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsClawbackTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testClawback")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const amountValue = canonical_AMOUNT(); + auto const holderValue = canonical_ACCOUNT(); + + ClawbackBuilder builder{ + accountValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setHolder(holderValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = holderValue; + auto const actualOpt = tx.getHolder(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfHolder should be present"; + expectEqualField(expected, *actualOpt, "sfHolder"); + EXPECT_TRUE(tx.hasHolder()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsClawbackTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testClawbackFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const amountValue = canonical_AMOUNT(); + auto const holderValue = canonical_ACCOUNT(); + + // Build an initial transaction + ClawbackBuilder initialBuilder{ + accountValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setHolder(holderValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + ClawbackBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = holderValue; + auto const actualOpt = rebuiltTx.getHolder(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfHolder should be present"; + expectEqualField(expected, *actualOpt, "sfHolder"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsClawbackTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(Clawback{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsClawbackTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(ClawbackBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsClawbackTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testClawbackNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const amountValue = canonical_AMOUNT(); + + ClawbackBuilder builder{ + accountValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasHolder()); + EXPECT_FALSE(tx.getHolder().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/CredentialAcceptTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/CredentialAcceptTests.cpp new file mode 100644 index 0000000000..5ae9c8a181 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/CredentialAcceptTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction CredentialAccept + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsCredentialAcceptTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialAccept")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const issuerValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + + CredentialAcceptBuilder builder{ + accountValue, + issuerValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = issuerValue; + auto const actual = tx.getIssuer(); + expectEqualField(expected, actual, "sfIssuer"); + } + + { + auto const& expected = credentialTypeValue; + auto const actual = tx.getCredentialType(); + expectEqualField(expected, actual, "sfCredentialType"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsCredentialAcceptTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialAcceptFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const issuerValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + + // Build an initial transaction + CredentialAcceptBuilder initialBuilder{ + accountValue, + issuerValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + CredentialAcceptBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = issuerValue; + auto const actual = rebuiltTx.getIssuer(); + expectEqualField(expected, actual, "sfIssuer"); + } + + { + auto const& expected = credentialTypeValue; + auto const actual = rebuiltTx.getCredentialType(); + expectEqualField(expected, actual, "sfCredentialType"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsCredentialAcceptTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CredentialAccept{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsCredentialAcceptTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CredentialAcceptBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/CredentialCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/CredentialCreateTests.cpp new file mode 100644 index 0000000000..0bfa9def33 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/CredentialCreateTests.cpp @@ -0,0 +1,234 @@ +// Auto-generated unit tests for transaction CredentialCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsCredentialCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const subjectValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + auto const expirationValue = canonical_UINT32(); + auto const uRIValue = canonical_VL(); + + CredentialCreateBuilder builder{ + accountValue, + subjectValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setExpiration(expirationValue); + builder.setURI(uRIValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = subjectValue; + auto const actual = tx.getSubject(); + expectEqualField(expected, actual, "sfSubject"); + } + + { + auto const& expected = credentialTypeValue; + auto const actual = tx.getCredentialType(); + expectEqualField(expected, actual, "sfCredentialType"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = tx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(tx.hasExpiration()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = tx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(tx.hasURI()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsCredentialCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const subjectValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + auto const expirationValue = canonical_UINT32(); + auto const uRIValue = canonical_VL(); + + // Build an initial transaction + CredentialCreateBuilder initialBuilder{ + accountValue, + subjectValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + initialBuilder.setExpiration(expirationValue); + initialBuilder.setURI(uRIValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + CredentialCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = subjectValue; + auto const actual = rebuiltTx.getSubject(); + expectEqualField(expected, actual, "sfSubject"); + } + + { + auto const& expected = credentialTypeValue; + auto const actual = rebuiltTx.getCredentialType(); + expectEqualField(expected, actual, "sfCredentialType"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = rebuiltTx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = rebuiltTx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsCredentialCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CredentialCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsCredentialCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CredentialCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsCredentialCreateTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialCreateNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const subjectValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + + CredentialCreateBuilder builder{ + accountValue, + subjectValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasExpiration()); + EXPECT_FALSE(tx.getExpiration().has_value()); + EXPECT_FALSE(tx.hasURI()); + EXPECT_FALSE(tx.getURI().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/CredentialDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/CredentialDeleteTests.cpp new file mode 100644 index 0000000000..26bba2ba1a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/CredentialDeleteTests.cpp @@ -0,0 +1,216 @@ +// Auto-generated unit tests for transaction CredentialDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsCredentialDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const subjectValue = canonical_ACCOUNT(); + auto const issuerValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + + CredentialDeleteBuilder builder{ + accountValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setSubject(subjectValue); + builder.setIssuer(issuerValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = credentialTypeValue; + auto const actual = tx.getCredentialType(); + expectEqualField(expected, actual, "sfCredentialType"); + } + + // Verify optional fields + { + auto const& expected = subjectValue; + auto const actualOpt = tx.getSubject(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSubject should be present"; + expectEqualField(expected, *actualOpt, "sfSubject"); + EXPECT_TRUE(tx.hasSubject()); + } + + { + auto const& expected = issuerValue; + auto const actualOpt = tx.getIssuer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfIssuer should be present"; + expectEqualField(expected, *actualOpt, "sfIssuer"); + EXPECT_TRUE(tx.hasIssuer()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsCredentialDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const subjectValue = canonical_ACCOUNT(); + auto const issuerValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + + // Build an initial transaction + CredentialDeleteBuilder initialBuilder{ + accountValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + initialBuilder.setSubject(subjectValue); + initialBuilder.setIssuer(issuerValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + CredentialDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = credentialTypeValue; + auto const actual = rebuiltTx.getCredentialType(); + expectEqualField(expected, actual, "sfCredentialType"); + } + + // Verify optional fields + { + auto const& expected = subjectValue; + auto const actualOpt = rebuiltTx.getSubject(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSubject should be present"; + expectEqualField(expected, *actualOpt, "sfSubject"); + } + + { + auto const& expected = issuerValue; + auto const actualOpt = rebuiltTx.getIssuer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfIssuer should be present"; + expectEqualField(expected, *actualOpt, "sfIssuer"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsCredentialDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CredentialDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsCredentialDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CredentialDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsCredentialDeleteTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialDeleteNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const credentialTypeValue = canonical_VL(); + + CredentialDeleteBuilder builder{ + accountValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasSubject()); + EXPECT_FALSE(tx.getSubject().has_value()); + EXPECT_FALSE(tx.hasIssuer()); + EXPECT_FALSE(tx.getIssuer().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/DIDDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/DIDDeleteTests.cpp new file mode 100644 index 0000000000..e60ef99fb1 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/DIDDeleteTests.cpp @@ -0,0 +1,130 @@ +// Auto-generated unit tests for transaction DIDDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsDIDDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDIDDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + + DIDDeleteBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsDIDDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDIDDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + + // Build an initial transaction + DIDDeleteBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + DIDDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsDIDDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DIDDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsDIDDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DIDDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/DIDSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/DIDSetTests.cpp new file mode 100644 index 0000000000..07215e6966 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/DIDSetTests.cpp @@ -0,0 +1,219 @@ +// Auto-generated unit tests for transaction DIDSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsDIDSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDIDSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const dIDDocumentValue = canonical_VL(); + auto const uRIValue = canonical_VL(); + auto const dataValue = canonical_VL(); + + DIDSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDIDDocument(dIDDocumentValue); + builder.setURI(uRIValue); + builder.setData(dataValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = dIDDocumentValue; + auto const actualOpt = tx.getDIDDocument(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDIDDocument should be present"; + expectEqualField(expected, *actualOpt, "sfDIDDocument"); + EXPECT_TRUE(tx.hasDIDDocument()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = tx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(tx.hasURI()); + } + + { + auto const& expected = dataValue; + auto const actualOpt = tx.getData(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present"; + expectEqualField(expected, *actualOpt, "sfData"); + EXPECT_TRUE(tx.hasData()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsDIDSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDIDSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const dIDDocumentValue = canonical_VL(); + auto const uRIValue = canonical_VL(); + auto const dataValue = canonical_VL(); + + // Build an initial transaction + DIDSetBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDIDDocument(dIDDocumentValue); + initialBuilder.setURI(uRIValue); + initialBuilder.setData(dataValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + DIDSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = dIDDocumentValue; + auto const actualOpt = rebuiltTx.getDIDDocument(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDIDDocument should be present"; + expectEqualField(expected, *actualOpt, "sfDIDDocument"); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = rebuiltTx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + } + + { + auto const& expected = dataValue; + auto const actualOpt = rebuiltTx.getData(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present"; + expectEqualField(expected, *actualOpt, "sfData"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsDIDSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DIDSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsDIDSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DIDSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsDIDSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDIDSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + DIDSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasDIDDocument()); + EXPECT_FALSE(tx.getDIDDocument().has_value()); + EXPECT_FALSE(tx.hasURI()); + EXPECT_FALSE(tx.getURI().has_value()); + EXPECT_FALSE(tx.hasData()); + EXPECT_FALSE(tx.getData().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/DelegateSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/DelegateSetTests.cpp new file mode 100644 index 0000000000..8e7fed9419 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/DelegateSetTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction DelegateSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsDelegateSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDelegateSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const authorizeValue = canonical_ACCOUNT(); + auto const permissionsValue = canonical_ARRAY(); + + DelegateSetBuilder builder{ + accountValue, + authorizeValue, + permissionsValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = authorizeValue; + auto const actual = tx.getAuthorize(); + expectEqualField(expected, actual, "sfAuthorize"); + } + + { + auto const& expected = permissionsValue; + auto const actual = tx.getPermissions(); + expectEqualField(expected, actual, "sfPermissions"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsDelegateSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDelegateSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const authorizeValue = canonical_ACCOUNT(); + auto const permissionsValue = canonical_ARRAY(); + + // Build an initial transaction + DelegateSetBuilder initialBuilder{ + accountValue, + authorizeValue, + permissionsValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + DelegateSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = authorizeValue; + auto const actual = rebuiltTx.getAuthorize(); + expectEqualField(expected, actual, "sfAuthorize"); + } + + { + auto const& expected = permissionsValue; + auto const actual = rebuiltTx.getPermissions(); + expectEqualField(expected, actual, "sfPermissions"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsDelegateSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DelegateSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsDelegateSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DelegateSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/DepositPreauthTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/DepositPreauthTests.cpp new file mode 100644 index 0000000000..36232eb220 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/DepositPreauthTests.cpp @@ -0,0 +1,240 @@ +// Auto-generated unit tests for transaction DepositPreauth + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsDepositPreauthTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDepositPreauth")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const authorizeValue = canonical_ACCOUNT(); + auto const unauthorizeValue = canonical_ACCOUNT(); + auto const authorizeCredentialsValue = canonical_ARRAY(); + auto const unauthorizeCredentialsValue = canonical_ARRAY(); + + DepositPreauthBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAuthorize(authorizeValue); + builder.setUnauthorize(unauthorizeValue); + builder.setAuthorizeCredentials(authorizeCredentialsValue); + builder.setUnauthorizeCredentials(unauthorizeCredentialsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = authorizeValue; + auto const actualOpt = tx.getAuthorize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAuthorize should be present"; + expectEqualField(expected, *actualOpt, "sfAuthorize"); + EXPECT_TRUE(tx.hasAuthorize()); + } + + { + auto const& expected = unauthorizeValue; + auto const actualOpt = tx.getUnauthorize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfUnauthorize should be present"; + expectEqualField(expected, *actualOpt, "sfUnauthorize"); + EXPECT_TRUE(tx.hasUnauthorize()); + } + + { + auto const& expected = authorizeCredentialsValue; + auto const actualOpt = tx.getAuthorizeCredentials(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAuthorizeCredentials should be present"; + expectEqualField(expected, *actualOpt, "sfAuthorizeCredentials"); + EXPECT_TRUE(tx.hasAuthorizeCredentials()); + } + + { + auto const& expected = unauthorizeCredentialsValue; + auto const actualOpt = tx.getUnauthorizeCredentials(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfUnauthorizeCredentials should be present"; + expectEqualField(expected, *actualOpt, "sfUnauthorizeCredentials"); + EXPECT_TRUE(tx.hasUnauthorizeCredentials()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsDepositPreauthTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDepositPreauthFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const authorizeValue = canonical_ACCOUNT(); + auto const unauthorizeValue = canonical_ACCOUNT(); + auto const authorizeCredentialsValue = canonical_ARRAY(); + auto const unauthorizeCredentialsValue = canonical_ARRAY(); + + // Build an initial transaction + DepositPreauthBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setAuthorize(authorizeValue); + initialBuilder.setUnauthorize(unauthorizeValue); + initialBuilder.setAuthorizeCredentials(authorizeCredentialsValue); + initialBuilder.setUnauthorizeCredentials(unauthorizeCredentialsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + DepositPreauthBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = authorizeValue; + auto const actualOpt = rebuiltTx.getAuthorize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAuthorize should be present"; + expectEqualField(expected, *actualOpt, "sfAuthorize"); + } + + { + auto const& expected = unauthorizeValue; + auto const actualOpt = rebuiltTx.getUnauthorize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfUnauthorize should be present"; + expectEqualField(expected, *actualOpt, "sfUnauthorize"); + } + + { + auto const& expected = authorizeCredentialsValue; + auto const actualOpt = rebuiltTx.getAuthorizeCredentials(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAuthorizeCredentials should be present"; + expectEqualField(expected, *actualOpt, "sfAuthorizeCredentials"); + } + + { + auto const& expected = unauthorizeCredentialsValue; + auto const actualOpt = rebuiltTx.getUnauthorizeCredentials(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfUnauthorizeCredentials should be present"; + expectEqualField(expected, *actualOpt, "sfUnauthorizeCredentials"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsDepositPreauthTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DepositPreauth{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsDepositPreauthTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DepositPreauthBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsDepositPreauthTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDepositPreauthNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + DepositPreauthBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAuthorize()); + EXPECT_FALSE(tx.getAuthorize().has_value()); + EXPECT_FALSE(tx.hasUnauthorize()); + EXPECT_FALSE(tx.getUnauthorize().has_value()); + EXPECT_FALSE(tx.hasAuthorizeCredentials()); + EXPECT_FALSE(tx.getAuthorizeCredentials().has_value()); + EXPECT_FALSE(tx.hasUnauthorizeCredentials()); + EXPECT_FALSE(tx.getUnauthorizeCredentials().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/EnableAmendmentTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/EnableAmendmentTests.cpp new file mode 100644 index 0000000000..e9dfe892e4 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/EnableAmendmentTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction EnableAmendment + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsEnableAmendmentTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEnableAmendment")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ledgerSequenceValue = canonical_UINT32(); + auto const amendmentValue = canonical_UINT256(); + + EnableAmendmentBuilder builder{ + accountValue, + ledgerSequenceValue, + amendmentValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ledgerSequenceValue; + auto const actual = tx.getLedgerSequence(); + expectEqualField(expected, actual, "sfLedgerSequence"); + } + + { + auto const& expected = amendmentValue; + auto const actual = tx.getAmendment(); + expectEqualField(expected, actual, "sfAmendment"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsEnableAmendmentTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEnableAmendmentFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ledgerSequenceValue = canonical_UINT32(); + auto const amendmentValue = canonical_UINT256(); + + // Build an initial transaction + EnableAmendmentBuilder initialBuilder{ + accountValue, + ledgerSequenceValue, + amendmentValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + EnableAmendmentBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ledgerSequenceValue; + auto const actual = rebuiltTx.getLedgerSequence(); + expectEqualField(expected, actual, "sfLedgerSequence"); + } + + { + auto const& expected = amendmentValue; + auto const actual = rebuiltTx.getAmendment(); + expectEqualField(expected, actual, "sfAmendment"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsEnableAmendmentTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EnableAmendment{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsEnableAmendmentTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EnableAmendmentBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/EscrowCancelTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/EscrowCancelTests.cpp new file mode 100644 index 0000000000..300a9b7615 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/EscrowCancelTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction EscrowCancel + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsEscrowCancelTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowCancel")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ownerValue = canonical_ACCOUNT(); + auto const offerSequenceValue = canonical_UINT32(); + + EscrowCancelBuilder builder{ + accountValue, + ownerValue, + offerSequenceValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ownerValue; + auto const actual = tx.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = offerSequenceValue; + auto const actual = tx.getOfferSequence(); + expectEqualField(expected, actual, "sfOfferSequence"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsEscrowCancelTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowCancelFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ownerValue = canonical_ACCOUNT(); + auto const offerSequenceValue = canonical_UINT32(); + + // Build an initial transaction + EscrowCancelBuilder initialBuilder{ + accountValue, + ownerValue, + offerSequenceValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + EscrowCancelBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ownerValue; + auto const actual = rebuiltTx.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = offerSequenceValue; + auto const actual = rebuiltTx.getOfferSequence(); + expectEqualField(expected, actual, "sfOfferSequence"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsEscrowCancelTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EscrowCancel{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsEscrowCancelTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EscrowCancelBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/EscrowCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/EscrowCreateTests.cpp new file mode 100644 index 0000000000..1f9b3d30a9 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/EscrowCreateTests.cpp @@ -0,0 +1,276 @@ +// Auto-generated unit tests for transaction EscrowCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsEscrowCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const conditionValue = canonical_VL(); + auto const cancelAfterValue = canonical_UINT32(); + auto const finishAfterValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + + EscrowCreateBuilder builder{ + accountValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setCondition(conditionValue); + builder.setCancelAfter(cancelAfterValue); + builder.setFinishAfter(finishAfterValue); + builder.setDestinationTag(destinationTagValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = conditionValue; + auto const actualOpt = tx.getCondition(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCondition should be present"; + expectEqualField(expected, *actualOpt, "sfCondition"); + EXPECT_TRUE(tx.hasCondition()); + } + + { + auto const& expected = cancelAfterValue; + auto const actualOpt = tx.getCancelAfter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCancelAfter should be present"; + expectEqualField(expected, *actualOpt, "sfCancelAfter"); + EXPECT_TRUE(tx.hasCancelAfter()); + } + + { + auto const& expected = finishAfterValue; + auto const actualOpt = tx.getFinishAfter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfFinishAfter should be present"; + expectEqualField(expected, *actualOpt, "sfFinishAfter"); + EXPECT_TRUE(tx.hasFinishAfter()); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = tx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + EXPECT_TRUE(tx.hasDestinationTag()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsEscrowCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const conditionValue = canonical_VL(); + auto const cancelAfterValue = canonical_UINT32(); + auto const finishAfterValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + + // Build an initial transaction + EscrowCreateBuilder initialBuilder{ + accountValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setCondition(conditionValue); + initialBuilder.setCancelAfter(cancelAfterValue); + initialBuilder.setFinishAfter(finishAfterValue); + initialBuilder.setDestinationTag(destinationTagValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + EscrowCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = conditionValue; + auto const actualOpt = rebuiltTx.getCondition(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCondition should be present"; + expectEqualField(expected, *actualOpt, "sfCondition"); + } + + { + auto const& expected = cancelAfterValue; + auto const actualOpt = rebuiltTx.getCancelAfter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCancelAfter should be present"; + expectEqualField(expected, *actualOpt, "sfCancelAfter"); + } + + { + auto const& expected = finishAfterValue; + auto const actualOpt = rebuiltTx.getFinishAfter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfFinishAfter should be present"; + expectEqualField(expected, *actualOpt, "sfFinishAfter"); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = rebuiltTx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsEscrowCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EscrowCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsEscrowCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EscrowCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsEscrowCreateTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowCreateNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + + EscrowCreateBuilder builder{ + accountValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasCondition()); + EXPECT_FALSE(tx.getCondition().has_value()); + EXPECT_FALSE(tx.hasCancelAfter()); + EXPECT_FALSE(tx.getCancelAfter().has_value()); + EXPECT_FALSE(tx.hasFinishAfter()); + EXPECT_FALSE(tx.getFinishAfter().has_value()); + EXPECT_FALSE(tx.hasDestinationTag()); + EXPECT_FALSE(tx.getDestinationTag().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/EscrowFinishTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/EscrowFinishTests.cpp new file mode 100644 index 0000000000..f729d0fda3 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/EscrowFinishTests.cpp @@ -0,0 +1,255 @@ +// Auto-generated unit tests for transaction EscrowFinish + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsEscrowFinishTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowFinish")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ownerValue = canonical_ACCOUNT(); + auto const offerSequenceValue = canonical_UINT32(); + auto const fulfillmentValue = canonical_VL(); + auto const conditionValue = canonical_VL(); + auto const credentialIDsValue = canonical_VECTOR256(); + + EscrowFinishBuilder builder{ + accountValue, + ownerValue, + offerSequenceValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setFulfillment(fulfillmentValue); + builder.setCondition(conditionValue); + builder.setCredentialIDs(credentialIDsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ownerValue; + auto const actual = tx.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = offerSequenceValue; + auto const actual = tx.getOfferSequence(); + expectEqualField(expected, actual, "sfOfferSequence"); + } + + // Verify optional fields + { + auto const& expected = fulfillmentValue; + auto const actualOpt = tx.getFulfillment(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfFulfillment should be present"; + expectEqualField(expected, *actualOpt, "sfFulfillment"); + EXPECT_TRUE(tx.hasFulfillment()); + } + + { + auto const& expected = conditionValue; + auto const actualOpt = tx.getCondition(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCondition should be present"; + expectEqualField(expected, *actualOpt, "sfCondition"); + EXPECT_TRUE(tx.hasCondition()); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = tx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + EXPECT_TRUE(tx.hasCredentialIDs()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsEscrowFinishTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowFinishFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ownerValue = canonical_ACCOUNT(); + auto const offerSequenceValue = canonical_UINT32(); + auto const fulfillmentValue = canonical_VL(); + auto const conditionValue = canonical_VL(); + auto const credentialIDsValue = canonical_VECTOR256(); + + // Build an initial transaction + EscrowFinishBuilder initialBuilder{ + accountValue, + ownerValue, + offerSequenceValue, + sequenceValue, + feeValue + }; + + initialBuilder.setFulfillment(fulfillmentValue); + initialBuilder.setCondition(conditionValue); + initialBuilder.setCredentialIDs(credentialIDsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + EscrowFinishBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ownerValue; + auto const actual = rebuiltTx.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = offerSequenceValue; + auto const actual = rebuiltTx.getOfferSequence(); + expectEqualField(expected, actual, "sfOfferSequence"); + } + + // Verify optional fields + { + auto const& expected = fulfillmentValue; + auto const actualOpt = rebuiltTx.getFulfillment(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfFulfillment should be present"; + expectEqualField(expected, *actualOpt, "sfFulfillment"); + } + + { + auto const& expected = conditionValue; + auto const actualOpt = rebuiltTx.getCondition(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCondition should be present"; + expectEqualField(expected, *actualOpt, "sfCondition"); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = rebuiltTx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsEscrowFinishTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EscrowFinish{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsEscrowFinishTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EscrowFinishBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsEscrowFinishTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowFinishNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const ownerValue = canonical_ACCOUNT(); + auto const offerSequenceValue = canonical_UINT32(); + + EscrowFinishBuilder builder{ + accountValue, + ownerValue, + offerSequenceValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasFulfillment()); + EXPECT_FALSE(tx.getFulfillment().has_value()); + EXPECT_FALSE(tx.hasCondition()); + EXPECT_FALSE(tx.getCondition().has_value()); + EXPECT_FALSE(tx.hasCredentialIDs()); + EXPECT_FALSE(tx.getCredentialIDs().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LedgerStateFixTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LedgerStateFixTests.cpp new file mode 100644 index 0000000000..b366605a4f --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LedgerStateFixTests.cpp @@ -0,0 +1,195 @@ +// Auto-generated unit tests for transaction LedgerStateFix + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLedgerStateFixTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLedgerStateFix")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ledgerFixTypeValue = canonical_UINT16(); + auto const ownerValue = canonical_ACCOUNT(); + + LedgerStateFixBuilder builder{ + accountValue, + ledgerFixTypeValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setOwner(ownerValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ledgerFixTypeValue; + auto const actual = tx.getLedgerFixType(); + expectEqualField(expected, actual, "sfLedgerFixType"); + } + + // Verify optional fields + { + auto const& expected = ownerValue; + auto const actualOpt = tx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + EXPECT_TRUE(tx.hasOwner()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLedgerStateFixTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLedgerStateFixFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ledgerFixTypeValue = canonical_UINT16(); + auto const ownerValue = canonical_ACCOUNT(); + + // Build an initial transaction + LedgerStateFixBuilder initialBuilder{ + accountValue, + ledgerFixTypeValue, + sequenceValue, + feeValue + }; + + initialBuilder.setOwner(ownerValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LedgerStateFixBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ledgerFixTypeValue; + auto const actual = rebuiltTx.getLedgerFixType(); + expectEqualField(expected, actual, "sfLedgerFixType"); + } + + // Verify optional fields + { + auto const& expected = ownerValue; + auto const actualOpt = rebuiltTx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLedgerStateFixTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LedgerStateFix{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLedgerStateFixTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LedgerStateFixBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsLedgerStateFixTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLedgerStateFixNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const ledgerFixTypeValue = canonical_UINT16(); + + LedgerStateFixBuilder builder{ + accountValue, + ledgerFixTypeValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasOwner()); + EXPECT_FALSE(tx.getOwner().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverClawbackTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverClawbackTests.cpp new file mode 100644 index 0000000000..1e6c72f458 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverClawbackTests.cpp @@ -0,0 +1,198 @@ +// Auto-generated unit tests for transaction LoanBrokerCoverClawback + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanBrokerCoverClawbackTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverClawback")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + LoanBrokerCoverClawbackBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setLoanBrokerID(loanBrokerIDValue); + builder.setAmount(amountValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = loanBrokerIDValue; + auto const actualOpt = tx.getLoanBrokerID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanBrokerID should be present"; + expectEqualField(expected, *actualOpt, "sfLoanBrokerID"); + EXPECT_TRUE(tx.hasLoanBrokerID()); + } + + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanBrokerCoverClawbackTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverClawbackFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + // Build an initial transaction + LoanBrokerCoverClawbackBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setLoanBrokerID(loanBrokerIDValue); + initialBuilder.setAmount(amountValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanBrokerCoverClawbackBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = loanBrokerIDValue; + auto const actualOpt = rebuiltTx.getLoanBrokerID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanBrokerID should be present"; + expectEqualField(expected, *actualOpt, "sfLoanBrokerID"); + } + + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerCoverClawbackTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerCoverClawback{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerCoverClawbackTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerCoverClawbackBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsLoanBrokerCoverClawbackTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverClawbackNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + LoanBrokerCoverClawbackBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasLoanBrokerID()); + EXPECT_FALSE(tx.getLoanBrokerID().has_value()); + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverDepositTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverDepositTests.cpp new file mode 100644 index 0000000000..d6addb2fef --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverDepositTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction LoanBrokerCoverDeposit + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanBrokerCoverDepositTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverDeposit")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + LoanBrokerCoverDepositBuilder builder{ + accountValue, + loanBrokerIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = tx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanBrokerCoverDepositTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverDepositFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + // Build an initial transaction + LoanBrokerCoverDepositBuilder initialBuilder{ + accountValue, + loanBrokerIDValue, + amountValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanBrokerCoverDepositBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = rebuiltTx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerCoverDepositTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerCoverDeposit{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerCoverDepositTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerCoverDepositBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverWithdrawTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverWithdrawTests.cpp new file mode 100644 index 0000000000..3e66944e39 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverWithdrawTests.cpp @@ -0,0 +1,234 @@ +// Auto-generated unit tests for transaction LoanBrokerCoverWithdraw + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanBrokerCoverWithdrawTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverWithdraw")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + + LoanBrokerCoverWithdrawBuilder builder{ + accountValue, + loanBrokerIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDestination(destinationValue); + builder.setDestinationTag(destinationTagValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = tx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = tx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + EXPECT_TRUE(tx.hasDestination()); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = tx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + EXPECT_TRUE(tx.hasDestinationTag()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanBrokerCoverWithdrawTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverWithdrawFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + + // Build an initial transaction + LoanBrokerCoverWithdrawBuilder initialBuilder{ + accountValue, + loanBrokerIDValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDestination(destinationValue); + initialBuilder.setDestinationTag(destinationTagValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanBrokerCoverWithdrawBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = rebuiltTx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = rebuiltTx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = rebuiltTx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerCoverWithdrawTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerCoverWithdraw{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerCoverWithdrawTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerCoverWithdrawBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsLoanBrokerCoverWithdrawTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverWithdrawNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + LoanBrokerCoverWithdrawBuilder builder{ + accountValue, + loanBrokerIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasDestination()); + EXPECT_FALSE(tx.getDestination().has_value()); + EXPECT_FALSE(tx.hasDestinationTag()); + EXPECT_FALSE(tx.getDestinationTag().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerDeleteTests.cpp new file mode 100644 index 0000000000..b557ab4397 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerDeleteTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction LoanBrokerDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanBrokerDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + + LoanBrokerDeleteBuilder builder{ + accountValue, + loanBrokerIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = tx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanBrokerDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + + // Build an initial transaction + LoanBrokerDeleteBuilder initialBuilder{ + accountValue, + loanBrokerIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanBrokerDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = rebuiltTx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerSetTests.cpp new file mode 100644 index 0000000000..e270332f42 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerSetTests.cpp @@ -0,0 +1,300 @@ +// Auto-generated unit tests for transaction LoanBrokerSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanBrokerSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const loanBrokerIDValue = canonical_UINT256(); + auto const dataValue = canonical_VL(); + auto const managementFeeRateValue = canonical_UINT16(); + auto const debtMaximumValue = canonical_NUMBER(); + auto const coverRateMinimumValue = canonical_UINT32(); + auto const coverRateLiquidationValue = canonical_UINT32(); + + LoanBrokerSetBuilder builder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setLoanBrokerID(loanBrokerIDValue); + builder.setData(dataValue); + builder.setManagementFeeRate(managementFeeRateValue); + builder.setDebtMaximum(debtMaximumValue); + builder.setCoverRateMinimum(coverRateMinimumValue); + builder.setCoverRateLiquidation(coverRateLiquidationValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = tx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + // Verify optional fields + { + auto const& expected = loanBrokerIDValue; + auto const actualOpt = tx.getLoanBrokerID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanBrokerID should be present"; + expectEqualField(expected, *actualOpt, "sfLoanBrokerID"); + EXPECT_TRUE(tx.hasLoanBrokerID()); + } + + { + auto const& expected = dataValue; + auto const actualOpt = tx.getData(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present"; + expectEqualField(expected, *actualOpt, "sfData"); + EXPECT_TRUE(tx.hasData()); + } + + { + auto const& expected = managementFeeRateValue; + auto const actualOpt = tx.getManagementFeeRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfManagementFeeRate should be present"; + expectEqualField(expected, *actualOpt, "sfManagementFeeRate"); + EXPECT_TRUE(tx.hasManagementFeeRate()); + } + + { + auto const& expected = debtMaximumValue; + auto const actualOpt = tx.getDebtMaximum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDebtMaximum should be present"; + expectEqualField(expected, *actualOpt, "sfDebtMaximum"); + EXPECT_TRUE(tx.hasDebtMaximum()); + } + + { + auto const& expected = coverRateMinimumValue; + auto const actualOpt = tx.getCoverRateMinimum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCoverRateMinimum should be present"; + expectEqualField(expected, *actualOpt, "sfCoverRateMinimum"); + EXPECT_TRUE(tx.hasCoverRateMinimum()); + } + + { + auto const& expected = coverRateLiquidationValue; + auto const actualOpt = tx.getCoverRateLiquidation(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCoverRateLiquidation should be present"; + expectEqualField(expected, *actualOpt, "sfCoverRateLiquidation"); + EXPECT_TRUE(tx.hasCoverRateLiquidation()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanBrokerSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const loanBrokerIDValue = canonical_UINT256(); + auto const dataValue = canonical_VL(); + auto const managementFeeRateValue = canonical_UINT16(); + auto const debtMaximumValue = canonical_NUMBER(); + auto const coverRateMinimumValue = canonical_UINT32(); + auto const coverRateLiquidationValue = canonical_UINT32(); + + // Build an initial transaction + LoanBrokerSetBuilder initialBuilder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setLoanBrokerID(loanBrokerIDValue); + initialBuilder.setData(dataValue); + initialBuilder.setManagementFeeRate(managementFeeRateValue); + initialBuilder.setDebtMaximum(debtMaximumValue); + initialBuilder.setCoverRateMinimum(coverRateMinimumValue); + initialBuilder.setCoverRateLiquidation(coverRateLiquidationValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanBrokerSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = rebuiltTx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + // Verify optional fields + { + auto const& expected = loanBrokerIDValue; + auto const actualOpt = rebuiltTx.getLoanBrokerID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanBrokerID should be present"; + expectEqualField(expected, *actualOpt, "sfLoanBrokerID"); + } + + { + auto const& expected = dataValue; + auto const actualOpt = rebuiltTx.getData(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present"; + expectEqualField(expected, *actualOpt, "sfData"); + } + + { + auto const& expected = managementFeeRateValue; + auto const actualOpt = rebuiltTx.getManagementFeeRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfManagementFeeRate should be present"; + expectEqualField(expected, *actualOpt, "sfManagementFeeRate"); + } + + { + auto const& expected = debtMaximumValue; + auto const actualOpt = rebuiltTx.getDebtMaximum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDebtMaximum should be present"; + expectEqualField(expected, *actualOpt, "sfDebtMaximum"); + } + + { + auto const& expected = coverRateMinimumValue; + auto const actualOpt = rebuiltTx.getCoverRateMinimum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCoverRateMinimum should be present"; + expectEqualField(expected, *actualOpt, "sfCoverRateMinimum"); + } + + { + auto const& expected = coverRateLiquidationValue; + auto const actualOpt = rebuiltTx.getCoverRateLiquidation(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCoverRateLiquidation should be present"; + expectEqualField(expected, *actualOpt, "sfCoverRateLiquidation"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsLoanBrokerSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const vaultIDValue = canonical_UINT256(); + + LoanBrokerSetBuilder builder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasLoanBrokerID()); + EXPECT_FALSE(tx.getLoanBrokerID().has_value()); + EXPECT_FALSE(tx.hasData()); + EXPECT_FALSE(tx.getData().has_value()); + EXPECT_FALSE(tx.hasManagementFeeRate()); + EXPECT_FALSE(tx.getManagementFeeRate().has_value()); + EXPECT_FALSE(tx.hasDebtMaximum()); + EXPECT_FALSE(tx.getDebtMaximum().has_value()); + EXPECT_FALSE(tx.hasCoverRateMinimum()); + EXPECT_FALSE(tx.getCoverRateMinimum().has_value()); + EXPECT_FALSE(tx.hasCoverRateLiquidation()); + EXPECT_FALSE(tx.getCoverRateLiquidation().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanDeleteTests.cpp new file mode 100644 index 0000000000..0edcdf4d06 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanDeleteTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction LoanDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanIDValue = canonical_UINT256(); + + LoanDeleteBuilder builder{ + accountValue, + loanIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanIDValue; + auto const actual = tx.getLoanID(); + expectEqualField(expected, actual, "sfLoanID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanIDValue = canonical_UINT256(); + + // Build an initial transaction + LoanDeleteBuilder initialBuilder{ + accountValue, + loanIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanIDValue; + auto const actual = rebuiltTx.getLoanID(); + expectEqualField(expected, actual, "sfLoanID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanManageTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanManageTests.cpp new file mode 100644 index 0000000000..bb842330d6 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanManageTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction LoanManage + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanManageTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanManage")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanIDValue = canonical_UINT256(); + + LoanManageBuilder builder{ + accountValue, + loanIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanIDValue; + auto const actual = tx.getLoanID(); + expectEqualField(expected, actual, "sfLoanID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanManageTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanManageFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanIDValue = canonical_UINT256(); + + // Build an initial transaction + LoanManageBuilder initialBuilder{ + accountValue, + loanIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanManageBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanIDValue; + auto const actual = rebuiltTx.getLoanID(); + expectEqualField(expected, actual, "sfLoanID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanManageTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanManage{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanManageTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanManageBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanPayTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanPayTests.cpp new file mode 100644 index 0000000000..2b0f16f25c --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanPayTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction LoanPay + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanPayTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanPay")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + LoanPayBuilder builder{ + accountValue, + loanIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanIDValue; + auto const actual = tx.getLoanID(); + expectEqualField(expected, actual, "sfLoanID"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanPayTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanPayFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + // Build an initial transaction + LoanPayBuilder initialBuilder{ + accountValue, + loanIDValue, + amountValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanPayBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanIDValue; + auto const actual = rebuiltTx.getLoanID(); + expectEqualField(expected, actual, "sfLoanID"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanPayTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanPay{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanPayTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanPayBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanSetTests.cpp new file mode 100644 index 0000000000..4141a958a0 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanSetTests.cpp @@ -0,0 +1,507 @@ +// Auto-generated unit tests for transaction LoanSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const dataValue = canonical_VL(); + auto const counterpartyValue = canonical_ACCOUNT(); + auto const counterpartySignatureValue = canonical_OBJECT(); + auto const loanOriginationFeeValue = canonical_NUMBER(); + auto const loanServiceFeeValue = canonical_NUMBER(); + auto const latePaymentFeeValue = canonical_NUMBER(); + auto const closePaymentFeeValue = canonical_NUMBER(); + auto const overpaymentFeeValue = canonical_UINT32(); + auto const interestRateValue = canonical_UINT32(); + auto const lateInterestRateValue = canonical_UINT32(); + auto const closeInterestRateValue = canonical_UINT32(); + auto const overpaymentInterestRateValue = canonical_UINT32(); + auto const principalRequestedValue = canonical_NUMBER(); + auto const paymentTotalValue = canonical_UINT32(); + auto const paymentIntervalValue = canonical_UINT32(); + auto const gracePeriodValue = canonical_UINT32(); + + LoanSetBuilder builder{ + accountValue, + loanBrokerIDValue, + principalRequestedValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setData(dataValue); + builder.setCounterparty(counterpartyValue); + builder.setCounterpartySignature(counterpartySignatureValue); + builder.setLoanOriginationFee(loanOriginationFeeValue); + builder.setLoanServiceFee(loanServiceFeeValue); + builder.setLatePaymentFee(latePaymentFeeValue); + builder.setClosePaymentFee(closePaymentFeeValue); + builder.setOverpaymentFee(overpaymentFeeValue); + builder.setInterestRate(interestRateValue); + builder.setLateInterestRate(lateInterestRateValue); + builder.setCloseInterestRate(closeInterestRateValue); + builder.setOverpaymentInterestRate(overpaymentInterestRateValue); + builder.setPaymentTotal(paymentTotalValue); + builder.setPaymentInterval(paymentIntervalValue); + builder.setGracePeriod(gracePeriodValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = tx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + { + auto const& expected = principalRequestedValue; + auto const actual = tx.getPrincipalRequested(); + expectEqualField(expected, actual, "sfPrincipalRequested"); + } + + // Verify optional fields + { + auto const& expected = dataValue; + auto const actualOpt = tx.getData(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present"; + expectEqualField(expected, *actualOpt, "sfData"); + EXPECT_TRUE(tx.hasData()); + } + + { + auto const& expected = counterpartyValue; + auto const actualOpt = tx.getCounterparty(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterparty should be present"; + expectEqualField(expected, *actualOpt, "sfCounterparty"); + EXPECT_TRUE(tx.hasCounterparty()); + } + + { + auto const& expected = counterpartySignatureValue; + auto const actualOpt = tx.getCounterpartySignature(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterpartySignature should be present"; + expectEqualField(expected, *actualOpt, "sfCounterpartySignature"); + EXPECT_TRUE(tx.hasCounterpartySignature()); + } + + { + auto const& expected = loanOriginationFeeValue; + auto const actualOpt = tx.getLoanOriginationFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanOriginationFee should be present"; + expectEqualField(expected, *actualOpt, "sfLoanOriginationFee"); + EXPECT_TRUE(tx.hasLoanOriginationFee()); + } + + { + auto const& expected = loanServiceFeeValue; + auto const actualOpt = tx.getLoanServiceFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanServiceFee should be present"; + expectEqualField(expected, *actualOpt, "sfLoanServiceFee"); + EXPECT_TRUE(tx.hasLoanServiceFee()); + } + + { + auto const& expected = latePaymentFeeValue; + auto const actualOpt = tx.getLatePaymentFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLatePaymentFee should be present"; + expectEqualField(expected, *actualOpt, "sfLatePaymentFee"); + EXPECT_TRUE(tx.hasLatePaymentFee()); + } + + { + auto const& expected = closePaymentFeeValue; + auto const actualOpt = tx.getClosePaymentFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfClosePaymentFee should be present"; + expectEqualField(expected, *actualOpt, "sfClosePaymentFee"); + EXPECT_TRUE(tx.hasClosePaymentFee()); + } + + { + auto const& expected = overpaymentFeeValue; + auto const actualOpt = tx.getOverpaymentFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOverpaymentFee should be present"; + expectEqualField(expected, *actualOpt, "sfOverpaymentFee"); + EXPECT_TRUE(tx.hasOverpaymentFee()); + } + + { + auto const& expected = interestRateValue; + auto const actualOpt = tx.getInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfInterestRate"); + EXPECT_TRUE(tx.hasInterestRate()); + } + + { + auto const& expected = lateInterestRateValue; + auto const actualOpt = tx.getLateInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLateInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfLateInterestRate"); + EXPECT_TRUE(tx.hasLateInterestRate()); + } + + { + auto const& expected = closeInterestRateValue; + auto const actualOpt = tx.getCloseInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCloseInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfCloseInterestRate"); + EXPECT_TRUE(tx.hasCloseInterestRate()); + } + + { + auto const& expected = overpaymentInterestRateValue; + auto const actualOpt = tx.getOverpaymentInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOverpaymentInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfOverpaymentInterestRate"); + EXPECT_TRUE(tx.hasOverpaymentInterestRate()); + } + + { + auto const& expected = paymentTotalValue; + auto const actualOpt = tx.getPaymentTotal(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaymentTotal should be present"; + expectEqualField(expected, *actualOpt, "sfPaymentTotal"); + EXPECT_TRUE(tx.hasPaymentTotal()); + } + + { + auto const& expected = paymentIntervalValue; + auto const actualOpt = tx.getPaymentInterval(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaymentInterval should be present"; + expectEqualField(expected, *actualOpt, "sfPaymentInterval"); + EXPECT_TRUE(tx.hasPaymentInterval()); + } + + { + auto const& expected = gracePeriodValue; + auto const actualOpt = tx.getGracePeriod(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfGracePeriod should be present"; + expectEqualField(expected, *actualOpt, "sfGracePeriod"); + EXPECT_TRUE(tx.hasGracePeriod()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const dataValue = canonical_VL(); + auto const counterpartyValue = canonical_ACCOUNT(); + auto const counterpartySignatureValue = canonical_OBJECT(); + auto const loanOriginationFeeValue = canonical_NUMBER(); + auto const loanServiceFeeValue = canonical_NUMBER(); + auto const latePaymentFeeValue = canonical_NUMBER(); + auto const closePaymentFeeValue = canonical_NUMBER(); + auto const overpaymentFeeValue = canonical_UINT32(); + auto const interestRateValue = canonical_UINT32(); + auto const lateInterestRateValue = canonical_UINT32(); + auto const closeInterestRateValue = canonical_UINT32(); + auto const overpaymentInterestRateValue = canonical_UINT32(); + auto const principalRequestedValue = canonical_NUMBER(); + auto const paymentTotalValue = canonical_UINT32(); + auto const paymentIntervalValue = canonical_UINT32(); + auto const gracePeriodValue = canonical_UINT32(); + + // Build an initial transaction + LoanSetBuilder initialBuilder{ + accountValue, + loanBrokerIDValue, + principalRequestedValue, + sequenceValue, + feeValue + }; + + initialBuilder.setData(dataValue); + initialBuilder.setCounterparty(counterpartyValue); + initialBuilder.setCounterpartySignature(counterpartySignatureValue); + initialBuilder.setLoanOriginationFee(loanOriginationFeeValue); + initialBuilder.setLoanServiceFee(loanServiceFeeValue); + initialBuilder.setLatePaymentFee(latePaymentFeeValue); + initialBuilder.setClosePaymentFee(closePaymentFeeValue); + initialBuilder.setOverpaymentFee(overpaymentFeeValue); + initialBuilder.setInterestRate(interestRateValue); + initialBuilder.setLateInterestRate(lateInterestRateValue); + initialBuilder.setCloseInterestRate(closeInterestRateValue); + initialBuilder.setOverpaymentInterestRate(overpaymentInterestRateValue); + initialBuilder.setPaymentTotal(paymentTotalValue); + initialBuilder.setPaymentInterval(paymentIntervalValue); + initialBuilder.setGracePeriod(gracePeriodValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = rebuiltTx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + { + auto const& expected = principalRequestedValue; + auto const actual = rebuiltTx.getPrincipalRequested(); + expectEqualField(expected, actual, "sfPrincipalRequested"); + } + + // Verify optional fields + { + auto const& expected = dataValue; + auto const actualOpt = rebuiltTx.getData(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present"; + expectEqualField(expected, *actualOpt, "sfData"); + } + + { + auto const& expected = counterpartyValue; + auto const actualOpt = rebuiltTx.getCounterparty(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterparty should be present"; + expectEqualField(expected, *actualOpt, "sfCounterparty"); + } + + { + auto const& expected = counterpartySignatureValue; + auto const actualOpt = rebuiltTx.getCounterpartySignature(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterpartySignature should be present"; + expectEqualField(expected, *actualOpt, "sfCounterpartySignature"); + } + + { + auto const& expected = loanOriginationFeeValue; + auto const actualOpt = rebuiltTx.getLoanOriginationFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanOriginationFee should be present"; + expectEqualField(expected, *actualOpt, "sfLoanOriginationFee"); + } + + { + auto const& expected = loanServiceFeeValue; + auto const actualOpt = rebuiltTx.getLoanServiceFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanServiceFee should be present"; + expectEqualField(expected, *actualOpt, "sfLoanServiceFee"); + } + + { + auto const& expected = latePaymentFeeValue; + auto const actualOpt = rebuiltTx.getLatePaymentFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLatePaymentFee should be present"; + expectEqualField(expected, *actualOpt, "sfLatePaymentFee"); + } + + { + auto const& expected = closePaymentFeeValue; + auto const actualOpt = rebuiltTx.getClosePaymentFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfClosePaymentFee should be present"; + expectEqualField(expected, *actualOpt, "sfClosePaymentFee"); + } + + { + auto const& expected = overpaymentFeeValue; + auto const actualOpt = rebuiltTx.getOverpaymentFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOverpaymentFee should be present"; + expectEqualField(expected, *actualOpt, "sfOverpaymentFee"); + } + + { + auto const& expected = interestRateValue; + auto const actualOpt = rebuiltTx.getInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfInterestRate"); + } + + { + auto const& expected = lateInterestRateValue; + auto const actualOpt = rebuiltTx.getLateInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLateInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfLateInterestRate"); + } + + { + auto const& expected = closeInterestRateValue; + auto const actualOpt = rebuiltTx.getCloseInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCloseInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfCloseInterestRate"); + } + + { + auto const& expected = overpaymentInterestRateValue; + auto const actualOpt = rebuiltTx.getOverpaymentInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOverpaymentInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfOverpaymentInterestRate"); + } + + { + auto const& expected = paymentTotalValue; + auto const actualOpt = rebuiltTx.getPaymentTotal(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaymentTotal should be present"; + expectEqualField(expected, *actualOpt, "sfPaymentTotal"); + } + + { + auto const& expected = paymentIntervalValue; + auto const actualOpt = rebuiltTx.getPaymentInterval(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaymentInterval should be present"; + expectEqualField(expected, *actualOpt, "sfPaymentInterval"); + } + + { + auto const& expected = gracePeriodValue; + auto const actualOpt = rebuiltTx.getGracePeriod(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfGracePeriod should be present"; + expectEqualField(expected, *actualOpt, "sfGracePeriod"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsLoanSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const principalRequestedValue = canonical_NUMBER(); + + LoanSetBuilder builder{ + accountValue, + loanBrokerIDValue, + principalRequestedValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasData()); + EXPECT_FALSE(tx.getData().has_value()); + EXPECT_FALSE(tx.hasCounterparty()); + EXPECT_FALSE(tx.getCounterparty().has_value()); + EXPECT_FALSE(tx.hasCounterpartySignature()); + EXPECT_FALSE(tx.getCounterpartySignature().has_value()); + EXPECT_FALSE(tx.hasLoanOriginationFee()); + EXPECT_FALSE(tx.getLoanOriginationFee().has_value()); + EXPECT_FALSE(tx.hasLoanServiceFee()); + EXPECT_FALSE(tx.getLoanServiceFee().has_value()); + EXPECT_FALSE(tx.hasLatePaymentFee()); + EXPECT_FALSE(tx.getLatePaymentFee().has_value()); + EXPECT_FALSE(tx.hasClosePaymentFee()); + EXPECT_FALSE(tx.getClosePaymentFee().has_value()); + EXPECT_FALSE(tx.hasOverpaymentFee()); + EXPECT_FALSE(tx.getOverpaymentFee().has_value()); + EXPECT_FALSE(tx.hasInterestRate()); + EXPECT_FALSE(tx.getInterestRate().has_value()); + EXPECT_FALSE(tx.hasLateInterestRate()); + EXPECT_FALSE(tx.getLateInterestRate().has_value()); + EXPECT_FALSE(tx.hasCloseInterestRate()); + EXPECT_FALSE(tx.getCloseInterestRate().has_value()); + EXPECT_FALSE(tx.hasOverpaymentInterestRate()); + EXPECT_FALSE(tx.getOverpaymentInterestRate().has_value()); + EXPECT_FALSE(tx.hasPaymentTotal()); + EXPECT_FALSE(tx.getPaymentTotal().has_value()); + EXPECT_FALSE(tx.hasPaymentInterval()); + EXPECT_FALSE(tx.getPaymentInterval().has_value()); + EXPECT_FALSE(tx.hasGracePeriod()); + EXPECT_FALSE(tx.getGracePeriod().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/MPTokenAuthorizeTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenAuthorizeTests.cpp new file mode 100644 index 0000000000..bde6df05ff --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenAuthorizeTests.cpp @@ -0,0 +1,195 @@ +// Auto-generated unit tests for transaction MPTokenAuthorize + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsMPTokenAuthorizeTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenAuthorize")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const holderValue = canonical_ACCOUNT(); + + MPTokenAuthorizeBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setHolder(holderValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = tx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + // Verify optional fields + { + auto const& expected = holderValue; + auto const actualOpt = tx.getHolder(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfHolder should be present"; + expectEqualField(expected, *actualOpt, "sfHolder"); + EXPECT_TRUE(tx.hasHolder()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsMPTokenAuthorizeTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenAuthorizeFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const holderValue = canonical_ACCOUNT(); + + // Build an initial transaction + MPTokenAuthorizeBuilder initialBuilder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setHolder(holderValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + MPTokenAuthorizeBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = rebuiltTx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + // Verify optional fields + { + auto const& expected = holderValue; + auto const actualOpt = rebuiltTx.getHolder(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfHolder should be present"; + expectEqualField(expected, *actualOpt, "sfHolder"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenAuthorizeTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenAuthorize{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenAuthorizeTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenAuthorizeBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsMPTokenAuthorizeTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenAuthorizeNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + + MPTokenAuthorizeBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasHolder()); + EXPECT_FALSE(tx.getHolder().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceCreateTests.cpp new file mode 100644 index 0000000000..dd4874e268 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceCreateTests.cpp @@ -0,0 +1,282 @@ +// Auto-generated unit tests for transaction MPTokenIssuanceCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsMPTokenIssuanceCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetScaleValue = canonical_UINT8(); + auto const transferFeeValue = canonical_UINT16(); + auto const maximumAmountValue = canonical_UINT64(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const domainIDValue = canonical_UINT256(); + auto const mutableFlagsValue = canonical_UINT32(); + + MPTokenIssuanceCreateBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAssetScale(assetScaleValue); + builder.setTransferFee(transferFeeValue); + builder.setMaximumAmount(maximumAmountValue); + builder.setMPTokenMetadata(mPTokenMetadataValue); + builder.setDomainID(domainIDValue); + builder.setMutableFlags(mutableFlagsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = assetScaleValue; + auto const actualOpt = tx.getAssetScale(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetScale should be present"; + expectEqualField(expected, *actualOpt, "sfAssetScale"); + EXPECT_TRUE(tx.hasAssetScale()); + } + + { + auto const& expected = transferFeeValue; + auto const actualOpt = tx.getTransferFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferFee should be present"; + expectEqualField(expected, *actualOpt, "sfTransferFee"); + EXPECT_TRUE(tx.hasTransferFee()); + } + + { + auto const& expected = maximumAmountValue; + auto const actualOpt = tx.getMaximumAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMaximumAmount should be present"; + expectEqualField(expected, *actualOpt, "sfMaximumAmount"); + EXPECT_TRUE(tx.hasMaximumAmount()); + } + + { + auto const& expected = mPTokenMetadataValue; + auto const actualOpt = tx.getMPTokenMetadata(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMPTokenMetadata should be present"; + expectEqualField(expected, *actualOpt, "sfMPTokenMetadata"); + EXPECT_TRUE(tx.hasMPTokenMetadata()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = tx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(tx.hasDomainID()); + } + + { + auto const& expected = mutableFlagsValue; + auto const actualOpt = tx.getMutableFlags(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present"; + expectEqualField(expected, *actualOpt, "sfMutableFlags"); + EXPECT_TRUE(tx.hasMutableFlags()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsMPTokenIssuanceCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetScaleValue = canonical_UINT8(); + auto const transferFeeValue = canonical_UINT16(); + auto const maximumAmountValue = canonical_UINT64(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const domainIDValue = canonical_UINT256(); + auto const mutableFlagsValue = canonical_UINT32(); + + // Build an initial transaction + MPTokenIssuanceCreateBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setAssetScale(assetScaleValue); + initialBuilder.setTransferFee(transferFeeValue); + initialBuilder.setMaximumAmount(maximumAmountValue); + initialBuilder.setMPTokenMetadata(mPTokenMetadataValue); + initialBuilder.setDomainID(domainIDValue); + initialBuilder.setMutableFlags(mutableFlagsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + MPTokenIssuanceCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = assetScaleValue; + auto const actualOpt = rebuiltTx.getAssetScale(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetScale should be present"; + expectEqualField(expected, *actualOpt, "sfAssetScale"); + } + + { + auto const& expected = transferFeeValue; + auto const actualOpt = rebuiltTx.getTransferFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferFee should be present"; + expectEqualField(expected, *actualOpt, "sfTransferFee"); + } + + { + auto const& expected = maximumAmountValue; + auto const actualOpt = rebuiltTx.getMaximumAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMaximumAmount should be present"; + expectEqualField(expected, *actualOpt, "sfMaximumAmount"); + } + + { + auto const& expected = mPTokenMetadataValue; + auto const actualOpt = rebuiltTx.getMPTokenMetadata(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMPTokenMetadata should be present"; + expectEqualField(expected, *actualOpt, "sfMPTokenMetadata"); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = rebuiltTx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + } + + { + auto const& expected = mutableFlagsValue; + auto const actualOpt = rebuiltTx.getMutableFlags(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present"; + expectEqualField(expected, *actualOpt, "sfMutableFlags"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenIssuanceCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenIssuanceCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenIssuanceCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenIssuanceCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsMPTokenIssuanceCreateTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceCreateNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + MPTokenIssuanceCreateBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAssetScale()); + EXPECT_FALSE(tx.getAssetScale().has_value()); + EXPECT_FALSE(tx.hasTransferFee()); + EXPECT_FALSE(tx.getTransferFee().has_value()); + EXPECT_FALSE(tx.hasMaximumAmount()); + EXPECT_FALSE(tx.getMaximumAmount().has_value()); + EXPECT_FALSE(tx.hasMPTokenMetadata()); + EXPECT_FALSE(tx.getMPTokenMetadata().has_value()); + EXPECT_FALSE(tx.hasDomainID()); + EXPECT_FALSE(tx.getDomainID().has_value()); + EXPECT_FALSE(tx.hasMutableFlags()); + EXPECT_FALSE(tx.getMutableFlags().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceDestroyTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceDestroyTests.cpp new file mode 100644 index 0000000000..8a69c1f63a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceDestroyTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction MPTokenIssuanceDestroy + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsMPTokenIssuanceDestroyTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceDestroy")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + + MPTokenIssuanceDestroyBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = tx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsMPTokenIssuanceDestroyTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceDestroyFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + + // Build an initial transaction + MPTokenIssuanceDestroyBuilder initialBuilder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + MPTokenIssuanceDestroyBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = rebuiltTx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenIssuanceDestroyTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenIssuanceDestroy{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenIssuanceDestroyTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenIssuanceDestroyBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceSetTests.cpp new file mode 100644 index 0000000000..2ed9e42184 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceSetTests.cpp @@ -0,0 +1,279 @@ +// Auto-generated unit tests for transaction MPTokenIssuanceSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsMPTokenIssuanceSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const holderValue = canonical_ACCOUNT(); + auto const domainIDValue = canonical_UINT256(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const transferFeeValue = canonical_UINT16(); + auto const mutableFlagsValue = canonical_UINT32(); + + MPTokenIssuanceSetBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setHolder(holderValue); + builder.setDomainID(domainIDValue); + builder.setMPTokenMetadata(mPTokenMetadataValue); + builder.setTransferFee(transferFeeValue); + builder.setMutableFlags(mutableFlagsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = tx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + // Verify optional fields + { + auto const& expected = holderValue; + auto const actualOpt = tx.getHolder(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfHolder should be present"; + expectEqualField(expected, *actualOpt, "sfHolder"); + EXPECT_TRUE(tx.hasHolder()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = tx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(tx.hasDomainID()); + } + + { + auto const& expected = mPTokenMetadataValue; + auto const actualOpt = tx.getMPTokenMetadata(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMPTokenMetadata should be present"; + expectEqualField(expected, *actualOpt, "sfMPTokenMetadata"); + EXPECT_TRUE(tx.hasMPTokenMetadata()); + } + + { + auto const& expected = transferFeeValue; + auto const actualOpt = tx.getTransferFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferFee should be present"; + expectEqualField(expected, *actualOpt, "sfTransferFee"); + EXPECT_TRUE(tx.hasTransferFee()); + } + + { + auto const& expected = mutableFlagsValue; + auto const actualOpt = tx.getMutableFlags(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present"; + expectEqualField(expected, *actualOpt, "sfMutableFlags"); + EXPECT_TRUE(tx.hasMutableFlags()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsMPTokenIssuanceSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const holderValue = canonical_ACCOUNT(); + auto const domainIDValue = canonical_UINT256(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const transferFeeValue = canonical_UINT16(); + auto const mutableFlagsValue = canonical_UINT32(); + + // Build an initial transaction + MPTokenIssuanceSetBuilder initialBuilder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setHolder(holderValue); + initialBuilder.setDomainID(domainIDValue); + initialBuilder.setMPTokenMetadata(mPTokenMetadataValue); + initialBuilder.setTransferFee(transferFeeValue); + initialBuilder.setMutableFlags(mutableFlagsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + MPTokenIssuanceSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = rebuiltTx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + // Verify optional fields + { + auto const& expected = holderValue; + auto const actualOpt = rebuiltTx.getHolder(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfHolder should be present"; + expectEqualField(expected, *actualOpt, "sfHolder"); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = rebuiltTx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + } + + { + auto const& expected = mPTokenMetadataValue; + auto const actualOpt = rebuiltTx.getMPTokenMetadata(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMPTokenMetadata should be present"; + expectEqualField(expected, *actualOpt, "sfMPTokenMetadata"); + } + + { + auto const& expected = transferFeeValue; + auto const actualOpt = rebuiltTx.getTransferFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferFee should be present"; + expectEqualField(expected, *actualOpt, "sfTransferFee"); + } + + { + auto const& expected = mutableFlagsValue; + auto const actualOpt = rebuiltTx.getMutableFlags(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present"; + expectEqualField(expected, *actualOpt, "sfMutableFlags"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenIssuanceSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenIssuanceSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenIssuanceSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenIssuanceSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsMPTokenIssuanceSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + + MPTokenIssuanceSetBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasHolder()); + EXPECT_FALSE(tx.getHolder().has_value()); + EXPECT_FALSE(tx.hasDomainID()); + EXPECT_FALSE(tx.getDomainID().has_value()); + EXPECT_FALSE(tx.hasMPTokenMetadata()); + EXPECT_FALSE(tx.getMPTokenMetadata().has_value()); + EXPECT_FALSE(tx.hasTransferFee()); + EXPECT_FALSE(tx.getTransferFee().has_value()); + EXPECT_FALSE(tx.hasMutableFlags()); + EXPECT_FALSE(tx.getMutableFlags().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/NFTokenAcceptOfferTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenAcceptOfferTests.cpp new file mode 100644 index 0000000000..e2fb8d29ec --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenAcceptOfferTests.cpp @@ -0,0 +1,219 @@ +// Auto-generated unit tests for transaction NFTokenAcceptOffer + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsNFTokenAcceptOfferTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenAcceptOffer")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenBuyOfferValue = canonical_UINT256(); + auto const nFTokenSellOfferValue = canonical_UINT256(); + auto const nFTokenBrokerFeeValue = canonical_AMOUNT(); + + NFTokenAcceptOfferBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setNFTokenBuyOffer(nFTokenBuyOfferValue); + builder.setNFTokenSellOffer(nFTokenSellOfferValue); + builder.setNFTokenBrokerFee(nFTokenBrokerFeeValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = nFTokenBuyOfferValue; + auto const actualOpt = tx.getNFTokenBuyOffer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenBuyOffer should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenBuyOffer"); + EXPECT_TRUE(tx.hasNFTokenBuyOffer()); + } + + { + auto const& expected = nFTokenSellOfferValue; + auto const actualOpt = tx.getNFTokenSellOffer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenSellOffer should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenSellOffer"); + EXPECT_TRUE(tx.hasNFTokenSellOffer()); + } + + { + auto const& expected = nFTokenBrokerFeeValue; + auto const actualOpt = tx.getNFTokenBrokerFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenBrokerFee should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenBrokerFee"); + EXPECT_TRUE(tx.hasNFTokenBrokerFee()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsNFTokenAcceptOfferTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenAcceptOfferFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenBuyOfferValue = canonical_UINT256(); + auto const nFTokenSellOfferValue = canonical_UINT256(); + auto const nFTokenBrokerFeeValue = canonical_AMOUNT(); + + // Build an initial transaction + NFTokenAcceptOfferBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setNFTokenBuyOffer(nFTokenBuyOfferValue); + initialBuilder.setNFTokenSellOffer(nFTokenSellOfferValue); + initialBuilder.setNFTokenBrokerFee(nFTokenBrokerFeeValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + NFTokenAcceptOfferBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = nFTokenBuyOfferValue; + auto const actualOpt = rebuiltTx.getNFTokenBuyOffer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenBuyOffer should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenBuyOffer"); + } + + { + auto const& expected = nFTokenSellOfferValue; + auto const actualOpt = rebuiltTx.getNFTokenSellOffer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenSellOffer should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenSellOffer"); + } + + { + auto const& expected = nFTokenBrokerFeeValue; + auto const actualOpt = rebuiltTx.getNFTokenBrokerFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenBrokerFee should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenBrokerFee"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenAcceptOfferTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenAcceptOffer{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenAcceptOfferTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenAcceptOfferBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsNFTokenAcceptOfferTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenAcceptOfferNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + NFTokenAcceptOfferBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasNFTokenBuyOffer()); + EXPECT_FALSE(tx.getNFTokenBuyOffer().has_value()); + EXPECT_FALSE(tx.hasNFTokenSellOffer()); + EXPECT_FALSE(tx.getNFTokenSellOffer().has_value()); + EXPECT_FALSE(tx.hasNFTokenBrokerFee()); + EXPECT_FALSE(tx.getNFTokenBrokerFee().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/NFTokenBurnTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenBurnTests.cpp new file mode 100644 index 0000000000..128ff51794 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenBurnTests.cpp @@ -0,0 +1,195 @@ +// Auto-generated unit tests for transaction NFTokenBurn + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsNFTokenBurnTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenBurn")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenIDValue = canonical_UINT256(); + auto const ownerValue = canonical_ACCOUNT(); + + NFTokenBurnBuilder builder{ + accountValue, + nFTokenIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setOwner(ownerValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenIDValue; + auto const actual = tx.getNFTokenID(); + expectEqualField(expected, actual, "sfNFTokenID"); + } + + // Verify optional fields + { + auto const& expected = ownerValue; + auto const actualOpt = tx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + EXPECT_TRUE(tx.hasOwner()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsNFTokenBurnTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenBurnFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenIDValue = canonical_UINT256(); + auto const ownerValue = canonical_ACCOUNT(); + + // Build an initial transaction + NFTokenBurnBuilder initialBuilder{ + accountValue, + nFTokenIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setOwner(ownerValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + NFTokenBurnBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenIDValue; + auto const actual = rebuiltTx.getNFTokenID(); + expectEqualField(expected, actual, "sfNFTokenID"); + } + + // Verify optional fields + { + auto const& expected = ownerValue; + auto const actualOpt = rebuiltTx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenBurnTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenBurn{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenBurnTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenBurnBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsNFTokenBurnTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenBurnNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const nFTokenIDValue = canonical_UINT256(); + + NFTokenBurnBuilder builder{ + accountValue, + nFTokenIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasOwner()); + EXPECT_FALSE(tx.getOwner().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/NFTokenCancelOfferTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenCancelOfferTests.cpp new file mode 100644 index 0000000000..f8ab0f7da1 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenCancelOfferTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction NFTokenCancelOffer + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsNFTokenCancelOfferTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenCancelOffer")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenOffersValue = canonical_VECTOR256(); + + NFTokenCancelOfferBuilder builder{ + accountValue, + nFTokenOffersValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenOffersValue; + auto const actual = tx.getNFTokenOffers(); + expectEqualField(expected, actual, "sfNFTokenOffers"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsNFTokenCancelOfferTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenCancelOfferFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenOffersValue = canonical_VECTOR256(); + + // Build an initial transaction + NFTokenCancelOfferBuilder initialBuilder{ + accountValue, + nFTokenOffersValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + NFTokenCancelOfferBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenOffersValue; + auto const actual = rebuiltTx.getNFTokenOffers(); + expectEqualField(expected, actual, "sfNFTokenOffers"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenCancelOfferTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenCancelOffer{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenCancelOfferTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenCancelOfferBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/NFTokenCreateOfferTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenCreateOfferTests.cpp new file mode 100644 index 0000000000..c57ede25c4 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenCreateOfferTests.cpp @@ -0,0 +1,255 @@ +// Auto-generated unit tests for transaction NFTokenCreateOffer + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsNFTokenCreateOfferTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenCreateOffer")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const ownerValue = canonical_ACCOUNT(); + auto const expirationValue = canonical_UINT32(); + + NFTokenCreateOfferBuilder builder{ + accountValue, + nFTokenIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDestination(destinationValue); + builder.setOwner(ownerValue); + builder.setExpiration(expirationValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenIDValue; + auto const actual = tx.getNFTokenID(); + expectEqualField(expected, actual, "sfNFTokenID"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = tx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + EXPECT_TRUE(tx.hasDestination()); + } + + { + auto const& expected = ownerValue; + auto const actualOpt = tx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + EXPECT_TRUE(tx.hasOwner()); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = tx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(tx.hasExpiration()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsNFTokenCreateOfferTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenCreateOfferFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const ownerValue = canonical_ACCOUNT(); + auto const expirationValue = canonical_UINT32(); + + // Build an initial transaction + NFTokenCreateOfferBuilder initialBuilder{ + accountValue, + nFTokenIDValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDestination(destinationValue); + initialBuilder.setOwner(ownerValue); + initialBuilder.setExpiration(expirationValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + NFTokenCreateOfferBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenIDValue; + auto const actual = rebuiltTx.getNFTokenID(); + expectEqualField(expected, actual, "sfNFTokenID"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = rebuiltTx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + } + + { + auto const& expected = ownerValue; + auto const actualOpt = rebuiltTx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = rebuiltTx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenCreateOfferTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenCreateOffer{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenCreateOfferTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenCreateOfferBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsNFTokenCreateOfferTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenCreateOfferNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const nFTokenIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + NFTokenCreateOfferBuilder builder{ + accountValue, + nFTokenIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasDestination()); + EXPECT_FALSE(tx.getDestination().has_value()); + EXPECT_FALSE(tx.hasOwner()); + EXPECT_FALSE(tx.getOwner().has_value()); + EXPECT_FALSE(tx.hasExpiration()); + EXPECT_FALSE(tx.getExpiration().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/NFTokenMintTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenMintTests.cpp new file mode 100644 index 0000000000..e85dbd238c --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenMintTests.cpp @@ -0,0 +1,300 @@ +// Auto-generated unit tests for transaction NFTokenMint + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsNFTokenMintTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenMint")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenTaxonValue = canonical_UINT32(); + auto const transferFeeValue = canonical_UINT16(); + auto const issuerValue = canonical_ACCOUNT(); + auto const uRIValue = canonical_VL(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const expirationValue = canonical_UINT32(); + + NFTokenMintBuilder builder{ + accountValue, + nFTokenTaxonValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setTransferFee(transferFeeValue); + builder.setIssuer(issuerValue); + builder.setURI(uRIValue); + builder.setAmount(amountValue); + builder.setDestination(destinationValue); + builder.setExpiration(expirationValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenTaxonValue; + auto const actual = tx.getNFTokenTaxon(); + expectEqualField(expected, actual, "sfNFTokenTaxon"); + } + + // Verify optional fields + { + auto const& expected = transferFeeValue; + auto const actualOpt = tx.getTransferFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferFee should be present"; + expectEqualField(expected, *actualOpt, "sfTransferFee"); + EXPECT_TRUE(tx.hasTransferFee()); + } + + { + auto const& expected = issuerValue; + auto const actualOpt = tx.getIssuer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfIssuer should be present"; + expectEqualField(expected, *actualOpt, "sfIssuer"); + EXPECT_TRUE(tx.hasIssuer()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = tx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(tx.hasURI()); + } + + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + + { + auto const& expected = destinationValue; + auto const actualOpt = tx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + EXPECT_TRUE(tx.hasDestination()); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = tx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(tx.hasExpiration()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsNFTokenMintTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenMintFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenTaxonValue = canonical_UINT32(); + auto const transferFeeValue = canonical_UINT16(); + auto const issuerValue = canonical_ACCOUNT(); + auto const uRIValue = canonical_VL(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const expirationValue = canonical_UINT32(); + + // Build an initial transaction + NFTokenMintBuilder initialBuilder{ + accountValue, + nFTokenTaxonValue, + sequenceValue, + feeValue + }; + + initialBuilder.setTransferFee(transferFeeValue); + initialBuilder.setIssuer(issuerValue); + initialBuilder.setURI(uRIValue); + initialBuilder.setAmount(amountValue); + initialBuilder.setDestination(destinationValue); + initialBuilder.setExpiration(expirationValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + NFTokenMintBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenTaxonValue; + auto const actual = rebuiltTx.getNFTokenTaxon(); + expectEqualField(expected, actual, "sfNFTokenTaxon"); + } + + // Verify optional fields + { + auto const& expected = transferFeeValue; + auto const actualOpt = rebuiltTx.getTransferFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferFee should be present"; + expectEqualField(expected, *actualOpt, "sfTransferFee"); + } + + { + auto const& expected = issuerValue; + auto const actualOpt = rebuiltTx.getIssuer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfIssuer should be present"; + expectEqualField(expected, *actualOpt, "sfIssuer"); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = rebuiltTx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + } + + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + + { + auto const& expected = destinationValue; + auto const actualOpt = rebuiltTx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = rebuiltTx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenMintTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenMint{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenMintTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenMintBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsNFTokenMintTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenMintNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const nFTokenTaxonValue = canonical_UINT32(); + + NFTokenMintBuilder builder{ + accountValue, + nFTokenTaxonValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasTransferFee()); + EXPECT_FALSE(tx.getTransferFee().has_value()); + EXPECT_FALSE(tx.hasIssuer()); + EXPECT_FALSE(tx.getIssuer().has_value()); + EXPECT_FALSE(tx.hasURI()); + EXPECT_FALSE(tx.getURI().has_value()); + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); + EXPECT_FALSE(tx.hasDestination()); + EXPECT_FALSE(tx.getDestination().has_value()); + EXPECT_FALSE(tx.hasExpiration()); + EXPECT_FALSE(tx.getExpiration().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/NFTokenModifyTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenModifyTests.cpp new file mode 100644 index 0000000000..9f5eeb052d --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenModifyTests.cpp @@ -0,0 +1,216 @@ +// Auto-generated unit tests for transaction NFTokenModify + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsNFTokenModifyTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenModify")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenIDValue = canonical_UINT256(); + auto const ownerValue = canonical_ACCOUNT(); + auto const uRIValue = canonical_VL(); + + NFTokenModifyBuilder builder{ + accountValue, + nFTokenIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setOwner(ownerValue); + builder.setURI(uRIValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenIDValue; + auto const actual = tx.getNFTokenID(); + expectEqualField(expected, actual, "sfNFTokenID"); + } + + // Verify optional fields + { + auto const& expected = ownerValue; + auto const actualOpt = tx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + EXPECT_TRUE(tx.hasOwner()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = tx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(tx.hasURI()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsNFTokenModifyTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenModifyFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenIDValue = canonical_UINT256(); + auto const ownerValue = canonical_ACCOUNT(); + auto const uRIValue = canonical_VL(); + + // Build an initial transaction + NFTokenModifyBuilder initialBuilder{ + accountValue, + nFTokenIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setOwner(ownerValue); + initialBuilder.setURI(uRIValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + NFTokenModifyBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenIDValue; + auto const actual = rebuiltTx.getNFTokenID(); + expectEqualField(expected, actual, "sfNFTokenID"); + } + + // Verify optional fields + { + auto const& expected = ownerValue; + auto const actualOpt = rebuiltTx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = rebuiltTx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenModifyTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenModify{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenModifyTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenModifyBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsNFTokenModifyTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenModifyNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const nFTokenIDValue = canonical_UINT256(); + + NFTokenModifyBuilder builder{ + accountValue, + nFTokenIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasOwner()); + EXPECT_FALSE(tx.getOwner().has_value()); + EXPECT_FALSE(tx.hasURI()); + EXPECT_FALSE(tx.getURI().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/OfferCancelTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/OfferCancelTests.cpp new file mode 100644 index 0000000000..0e3c8a4224 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/OfferCancelTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction OfferCancel + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsOfferCancelTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOfferCancel")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const offerSequenceValue = canonical_UINT32(); + + OfferCancelBuilder builder{ + accountValue, + offerSequenceValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = offerSequenceValue; + auto const actual = tx.getOfferSequence(); + expectEqualField(expected, actual, "sfOfferSequence"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsOfferCancelTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOfferCancelFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const offerSequenceValue = canonical_UINT32(); + + // Build an initial transaction + OfferCancelBuilder initialBuilder{ + accountValue, + offerSequenceValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + OfferCancelBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = offerSequenceValue; + auto const actual = rebuiltTx.getOfferSequence(); + expectEqualField(expected, actual, "sfOfferSequence"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsOfferCancelTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OfferCancel{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsOfferCancelTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OfferCancelBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/OfferCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/OfferCreateTests.cpp new file mode 100644 index 0000000000..3dde69574e --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/OfferCreateTests.cpp @@ -0,0 +1,255 @@ +// Auto-generated unit tests for transaction OfferCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsOfferCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOfferCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const takerPaysValue = canonical_AMOUNT(); + auto const takerGetsValue = canonical_AMOUNT(); + auto const expirationValue = canonical_UINT32(); + auto const offerSequenceValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + + OfferCreateBuilder builder{ + accountValue, + takerPaysValue, + takerGetsValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setExpiration(expirationValue); + builder.setOfferSequence(offerSequenceValue); + builder.setDomainID(domainIDValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = takerPaysValue; + auto const actual = tx.getTakerPays(); + expectEqualField(expected, actual, "sfTakerPays"); + } + + { + auto const& expected = takerGetsValue; + auto const actual = tx.getTakerGets(); + expectEqualField(expected, actual, "sfTakerGets"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = tx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(tx.hasExpiration()); + } + + { + auto const& expected = offerSequenceValue; + auto const actualOpt = tx.getOfferSequence(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOfferSequence should be present"; + expectEqualField(expected, *actualOpt, "sfOfferSequence"); + EXPECT_TRUE(tx.hasOfferSequence()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = tx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(tx.hasDomainID()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsOfferCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOfferCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const takerPaysValue = canonical_AMOUNT(); + auto const takerGetsValue = canonical_AMOUNT(); + auto const expirationValue = canonical_UINT32(); + auto const offerSequenceValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + + // Build an initial transaction + OfferCreateBuilder initialBuilder{ + accountValue, + takerPaysValue, + takerGetsValue, + sequenceValue, + feeValue + }; + + initialBuilder.setExpiration(expirationValue); + initialBuilder.setOfferSequence(offerSequenceValue); + initialBuilder.setDomainID(domainIDValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + OfferCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = takerPaysValue; + auto const actual = rebuiltTx.getTakerPays(); + expectEqualField(expected, actual, "sfTakerPays"); + } + + { + auto const& expected = takerGetsValue; + auto const actual = rebuiltTx.getTakerGets(); + expectEqualField(expected, actual, "sfTakerGets"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = rebuiltTx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + } + + { + auto const& expected = offerSequenceValue; + auto const actualOpt = rebuiltTx.getOfferSequence(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOfferSequence should be present"; + expectEqualField(expected, *actualOpt, "sfOfferSequence"); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = rebuiltTx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsOfferCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OfferCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsOfferCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OfferCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsOfferCreateTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOfferCreateNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const takerPaysValue = canonical_AMOUNT(); + auto const takerGetsValue = canonical_AMOUNT(); + + OfferCreateBuilder builder{ + accountValue, + takerPaysValue, + takerGetsValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasExpiration()); + EXPECT_FALSE(tx.getExpiration().has_value()); + EXPECT_FALSE(tx.hasOfferSequence()); + EXPECT_FALSE(tx.getOfferSequence().has_value()); + EXPECT_FALSE(tx.hasDomainID()); + EXPECT_FALSE(tx.getDomainID().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/OracleDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/OracleDeleteTests.cpp new file mode 100644 index 0000000000..631ad0861a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/OracleDeleteTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction OracleDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsOracleDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOracleDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const oracleDocumentIDValue = canonical_UINT32(); + + OracleDeleteBuilder builder{ + accountValue, + oracleDocumentIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = oracleDocumentIDValue; + auto const actual = tx.getOracleDocumentID(); + expectEqualField(expected, actual, "sfOracleDocumentID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsOracleDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOracleDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const oracleDocumentIDValue = canonical_UINT32(); + + // Build an initial transaction + OracleDeleteBuilder initialBuilder{ + accountValue, + oracleDocumentIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + OracleDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = oracleDocumentIDValue; + auto const actual = rebuiltTx.getOracleDocumentID(); + expectEqualField(expected, actual, "sfOracleDocumentID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsOracleDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OracleDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsOracleDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OracleDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/OracleSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/OracleSetTests.cpp new file mode 100644 index 0000000000..e13e8a8374 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/OracleSetTests.cpp @@ -0,0 +1,273 @@ +// Auto-generated unit tests for transaction OracleSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsOracleSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOracleSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const oracleDocumentIDValue = canonical_UINT32(); + auto const providerValue = canonical_VL(); + auto const uRIValue = canonical_VL(); + auto const assetClassValue = canonical_VL(); + auto const lastUpdateTimeValue = canonical_UINT32(); + auto const priceDataSeriesValue = canonical_ARRAY(); + + OracleSetBuilder builder{ + accountValue, + oracleDocumentIDValue, + lastUpdateTimeValue, + priceDataSeriesValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setProvider(providerValue); + builder.setURI(uRIValue); + builder.setAssetClass(assetClassValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = oracleDocumentIDValue; + auto const actual = tx.getOracleDocumentID(); + expectEqualField(expected, actual, "sfOracleDocumentID"); + } + + { + auto const& expected = lastUpdateTimeValue; + auto const actual = tx.getLastUpdateTime(); + expectEqualField(expected, actual, "sfLastUpdateTime"); + } + + { + auto const& expected = priceDataSeriesValue; + auto const actual = tx.getPriceDataSeries(); + expectEqualField(expected, actual, "sfPriceDataSeries"); + } + + // Verify optional fields + { + auto const& expected = providerValue; + auto const actualOpt = tx.getProvider(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfProvider should be present"; + expectEqualField(expected, *actualOpt, "sfProvider"); + EXPECT_TRUE(tx.hasProvider()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = tx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(tx.hasURI()); + } + + { + auto const& expected = assetClassValue; + auto const actualOpt = tx.getAssetClass(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetClass should be present"; + expectEqualField(expected, *actualOpt, "sfAssetClass"); + EXPECT_TRUE(tx.hasAssetClass()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsOracleSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOracleSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const oracleDocumentIDValue = canonical_UINT32(); + auto const providerValue = canonical_VL(); + auto const uRIValue = canonical_VL(); + auto const assetClassValue = canonical_VL(); + auto const lastUpdateTimeValue = canonical_UINT32(); + auto const priceDataSeriesValue = canonical_ARRAY(); + + // Build an initial transaction + OracleSetBuilder initialBuilder{ + accountValue, + oracleDocumentIDValue, + lastUpdateTimeValue, + priceDataSeriesValue, + sequenceValue, + feeValue + }; + + initialBuilder.setProvider(providerValue); + initialBuilder.setURI(uRIValue); + initialBuilder.setAssetClass(assetClassValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + OracleSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = oracleDocumentIDValue; + auto const actual = rebuiltTx.getOracleDocumentID(); + expectEqualField(expected, actual, "sfOracleDocumentID"); + } + + { + auto const& expected = lastUpdateTimeValue; + auto const actual = rebuiltTx.getLastUpdateTime(); + expectEqualField(expected, actual, "sfLastUpdateTime"); + } + + { + auto const& expected = priceDataSeriesValue; + auto const actual = rebuiltTx.getPriceDataSeries(); + expectEqualField(expected, actual, "sfPriceDataSeries"); + } + + // Verify optional fields + { + auto const& expected = providerValue; + auto const actualOpt = rebuiltTx.getProvider(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfProvider should be present"; + expectEqualField(expected, *actualOpt, "sfProvider"); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = rebuiltTx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + } + + { + auto const& expected = assetClassValue; + auto const actualOpt = rebuiltTx.getAssetClass(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetClass should be present"; + expectEqualField(expected, *actualOpt, "sfAssetClass"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsOracleSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OracleSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsOracleSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OracleSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsOracleSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOracleSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const oracleDocumentIDValue = canonical_UINT32(); + auto const lastUpdateTimeValue = canonical_UINT32(); + auto const priceDataSeriesValue = canonical_ARRAY(); + + OracleSetBuilder builder{ + accountValue, + oracleDocumentIDValue, + lastUpdateTimeValue, + priceDataSeriesValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasProvider()); + EXPECT_FALSE(tx.getProvider().has_value()); + EXPECT_FALSE(tx.hasURI()); + EXPECT_FALSE(tx.getURI().has_value()); + EXPECT_FALSE(tx.hasAssetClass()); + EXPECT_FALSE(tx.getAssetClass().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelClaimTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelClaimTests.cpp new file mode 100644 index 0000000000..1b742196a5 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelClaimTests.cpp @@ -0,0 +1,279 @@ +// Auto-generated unit tests for transaction PaymentChannelClaim + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsPaymentChannelClaimTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelClaim")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const channelValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const balanceValue = canonical_AMOUNT(); + auto const signatureValue = canonical_VL(); + auto const publicKeyValue = canonical_VL(); + auto const credentialIDsValue = canonical_VECTOR256(); + + PaymentChannelClaimBuilder builder{ + accountValue, + channelValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAmount(amountValue); + builder.setBalance(balanceValue); + builder.setSignature(signatureValue); + builder.setPublicKey(publicKeyValue); + builder.setCredentialIDs(credentialIDsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = channelValue; + auto const actual = tx.getChannel(); + expectEqualField(expected, actual, "sfChannel"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + + { + auto const& expected = balanceValue; + auto const actualOpt = tx.getBalance(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBalance should be present"; + expectEqualField(expected, *actualOpt, "sfBalance"); + EXPECT_TRUE(tx.hasBalance()); + } + + { + auto const& expected = signatureValue; + auto const actualOpt = tx.getSignature(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSignature should be present"; + expectEqualField(expected, *actualOpt, "sfSignature"); + EXPECT_TRUE(tx.hasSignature()); + } + + { + auto const& expected = publicKeyValue; + auto const actualOpt = tx.getPublicKey(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPublicKey should be present"; + expectEqualField(expected, *actualOpt, "sfPublicKey"); + EXPECT_TRUE(tx.hasPublicKey()); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = tx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + EXPECT_TRUE(tx.hasCredentialIDs()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsPaymentChannelClaimTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelClaimFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const channelValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const balanceValue = canonical_AMOUNT(); + auto const signatureValue = canonical_VL(); + auto const publicKeyValue = canonical_VL(); + auto const credentialIDsValue = canonical_VECTOR256(); + + // Build an initial transaction + PaymentChannelClaimBuilder initialBuilder{ + accountValue, + channelValue, + sequenceValue, + feeValue + }; + + initialBuilder.setAmount(amountValue); + initialBuilder.setBalance(balanceValue); + initialBuilder.setSignature(signatureValue); + initialBuilder.setPublicKey(publicKeyValue); + initialBuilder.setCredentialIDs(credentialIDsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + PaymentChannelClaimBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = channelValue; + auto const actual = rebuiltTx.getChannel(); + expectEqualField(expected, actual, "sfChannel"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + + { + auto const& expected = balanceValue; + auto const actualOpt = rebuiltTx.getBalance(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBalance should be present"; + expectEqualField(expected, *actualOpt, "sfBalance"); + } + + { + auto const& expected = signatureValue; + auto const actualOpt = rebuiltTx.getSignature(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSignature should be present"; + expectEqualField(expected, *actualOpt, "sfSignature"); + } + + { + auto const& expected = publicKeyValue; + auto const actualOpt = rebuiltTx.getPublicKey(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPublicKey should be present"; + expectEqualField(expected, *actualOpt, "sfPublicKey"); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = rebuiltTx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsPaymentChannelClaimTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PaymentChannelClaim{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsPaymentChannelClaimTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PaymentChannelClaimBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsPaymentChannelClaimTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelClaimNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const channelValue = canonical_UINT256(); + + PaymentChannelClaimBuilder builder{ + accountValue, + channelValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); + EXPECT_FALSE(tx.hasBalance()); + EXPECT_FALSE(tx.getBalance().has_value()); + EXPECT_FALSE(tx.hasSignature()); + EXPECT_FALSE(tx.getSignature().has_value()); + EXPECT_FALSE(tx.hasPublicKey()); + EXPECT_FALSE(tx.getPublicKey().has_value()); + EXPECT_FALSE(tx.hasCredentialIDs()); + EXPECT_FALSE(tx.getCredentialIDs().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelCreateTests.cpp new file mode 100644 index 0000000000..e3ba999d0b --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelCreateTests.cpp @@ -0,0 +1,270 @@ +// Auto-generated unit tests for transaction PaymentChannelCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsPaymentChannelCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const settleDelayValue = canonical_UINT32(); + auto const publicKeyValue = canonical_VL(); + auto const cancelAfterValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + + PaymentChannelCreateBuilder builder{ + accountValue, + destinationValue, + amountValue, + settleDelayValue, + publicKeyValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setCancelAfter(cancelAfterValue); + builder.setDestinationTag(destinationTagValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = settleDelayValue; + auto const actual = tx.getSettleDelay(); + expectEqualField(expected, actual, "sfSettleDelay"); + } + + { + auto const& expected = publicKeyValue; + auto const actual = tx.getPublicKey(); + expectEqualField(expected, actual, "sfPublicKey"); + } + + // Verify optional fields + { + auto const& expected = cancelAfterValue; + auto const actualOpt = tx.getCancelAfter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCancelAfter should be present"; + expectEqualField(expected, *actualOpt, "sfCancelAfter"); + EXPECT_TRUE(tx.hasCancelAfter()); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = tx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + EXPECT_TRUE(tx.hasDestinationTag()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsPaymentChannelCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const settleDelayValue = canonical_UINT32(); + auto const publicKeyValue = canonical_VL(); + auto const cancelAfterValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + + // Build an initial transaction + PaymentChannelCreateBuilder initialBuilder{ + accountValue, + destinationValue, + amountValue, + settleDelayValue, + publicKeyValue, + sequenceValue, + feeValue + }; + + initialBuilder.setCancelAfter(cancelAfterValue); + initialBuilder.setDestinationTag(destinationTagValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + PaymentChannelCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = settleDelayValue; + auto const actual = rebuiltTx.getSettleDelay(); + expectEqualField(expected, actual, "sfSettleDelay"); + } + + { + auto const& expected = publicKeyValue; + auto const actual = rebuiltTx.getPublicKey(); + expectEqualField(expected, actual, "sfPublicKey"); + } + + // Verify optional fields + { + auto const& expected = cancelAfterValue; + auto const actualOpt = rebuiltTx.getCancelAfter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCancelAfter should be present"; + expectEqualField(expected, *actualOpt, "sfCancelAfter"); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = rebuiltTx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsPaymentChannelCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PaymentChannelCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsPaymentChannelCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PaymentChannelCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsPaymentChannelCreateTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelCreateNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const settleDelayValue = canonical_UINT32(); + auto const publicKeyValue = canonical_VL(); + + PaymentChannelCreateBuilder builder{ + accountValue, + destinationValue, + amountValue, + settleDelayValue, + publicKeyValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasCancelAfter()); + EXPECT_FALSE(tx.getCancelAfter().has_value()); + EXPECT_FALSE(tx.hasDestinationTag()); + EXPECT_FALSE(tx.getDestinationTag().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelFundTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelFundTests.cpp new file mode 100644 index 0000000000..ce070c0761 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelFundTests.cpp @@ -0,0 +1,213 @@ +// Auto-generated unit tests for transaction PaymentChannelFund + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsPaymentChannelFundTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelFund")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const channelValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const expirationValue = canonical_UINT32(); + + PaymentChannelFundBuilder builder{ + accountValue, + channelValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setExpiration(expirationValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = channelValue; + auto const actual = tx.getChannel(); + expectEqualField(expected, actual, "sfChannel"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = tx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(tx.hasExpiration()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsPaymentChannelFundTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelFundFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const channelValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const expirationValue = canonical_UINT32(); + + // Build an initial transaction + PaymentChannelFundBuilder initialBuilder{ + accountValue, + channelValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setExpiration(expirationValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + PaymentChannelFundBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = channelValue; + auto const actual = rebuiltTx.getChannel(); + expectEqualField(expected, actual, "sfChannel"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = rebuiltTx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsPaymentChannelFundTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PaymentChannelFund{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsPaymentChannelFundTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PaymentChannelFundBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsPaymentChannelFundTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelFundNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const channelValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + PaymentChannelFundBuilder builder{ + accountValue, + channelValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasExpiration()); + EXPECT_FALSE(tx.getExpiration().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/PaymentTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/PaymentTests.cpp new file mode 100644 index 0000000000..7e5c046fca --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/PaymentTests.cpp @@ -0,0 +1,339 @@ +// Auto-generated unit tests for transaction Payment + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsPaymentTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPayment")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + auto const pathsValue = canonical_PATHSET(); + auto const invoiceIDValue = canonical_UINT256(); + auto const destinationTagValue = canonical_UINT32(); + auto const deliverMinValue = canonical_AMOUNT(); + auto const credentialIDsValue = canonical_VECTOR256(); + auto const domainIDValue = canonical_UINT256(); + + PaymentBuilder builder{ + accountValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setSendMax(sendMaxValue); + builder.setPaths(pathsValue); + builder.setInvoiceID(invoiceIDValue); + builder.setDestinationTag(destinationTagValue); + builder.setDeliverMin(deliverMinValue); + builder.setCredentialIDs(credentialIDsValue); + builder.setDomainID(domainIDValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = sendMaxValue; + auto const actualOpt = tx.getSendMax(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSendMax should be present"; + expectEqualField(expected, *actualOpt, "sfSendMax"); + EXPECT_TRUE(tx.hasSendMax()); + } + + { + auto const& expected = pathsValue; + auto const actualOpt = tx.getPaths(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaths should be present"; + expectEqualField(expected, *actualOpt, "sfPaths"); + EXPECT_TRUE(tx.hasPaths()); + } + + { + auto const& expected = invoiceIDValue; + auto const actualOpt = tx.getInvoiceID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInvoiceID should be present"; + expectEqualField(expected, *actualOpt, "sfInvoiceID"); + EXPECT_TRUE(tx.hasInvoiceID()); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = tx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + EXPECT_TRUE(tx.hasDestinationTag()); + } + + { + auto const& expected = deliverMinValue; + auto const actualOpt = tx.getDeliverMin(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDeliverMin should be present"; + expectEqualField(expected, *actualOpt, "sfDeliverMin"); + EXPECT_TRUE(tx.hasDeliverMin()); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = tx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + EXPECT_TRUE(tx.hasCredentialIDs()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = tx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(tx.hasDomainID()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsPaymentTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + auto const pathsValue = canonical_PATHSET(); + auto const invoiceIDValue = canonical_UINT256(); + auto const destinationTagValue = canonical_UINT32(); + auto const deliverMinValue = canonical_AMOUNT(); + auto const credentialIDsValue = canonical_VECTOR256(); + auto const domainIDValue = canonical_UINT256(); + + // Build an initial transaction + PaymentBuilder initialBuilder{ + accountValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setSendMax(sendMaxValue); + initialBuilder.setPaths(pathsValue); + initialBuilder.setInvoiceID(invoiceIDValue); + initialBuilder.setDestinationTag(destinationTagValue); + initialBuilder.setDeliverMin(deliverMinValue); + initialBuilder.setCredentialIDs(credentialIDsValue); + initialBuilder.setDomainID(domainIDValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + PaymentBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = sendMaxValue; + auto const actualOpt = rebuiltTx.getSendMax(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSendMax should be present"; + expectEqualField(expected, *actualOpt, "sfSendMax"); + } + + { + auto const& expected = pathsValue; + auto const actualOpt = rebuiltTx.getPaths(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaths should be present"; + expectEqualField(expected, *actualOpt, "sfPaths"); + } + + { + auto const& expected = invoiceIDValue; + auto const actualOpt = rebuiltTx.getInvoiceID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInvoiceID should be present"; + expectEqualField(expected, *actualOpt, "sfInvoiceID"); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = rebuiltTx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + } + + { + auto const& expected = deliverMinValue; + auto const actualOpt = rebuiltTx.getDeliverMin(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDeliverMin should be present"; + expectEqualField(expected, *actualOpt, "sfDeliverMin"); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = rebuiltTx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = rebuiltTx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsPaymentTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(Payment{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsPaymentTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PaymentBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsPaymentTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + + PaymentBuilder builder{ + accountValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasSendMax()); + EXPECT_FALSE(tx.getSendMax().has_value()); + EXPECT_FALSE(tx.hasPaths()); + EXPECT_FALSE(tx.getPaths().has_value()); + EXPECT_FALSE(tx.hasInvoiceID()); + EXPECT_FALSE(tx.getInvoiceID().has_value()); + EXPECT_FALSE(tx.hasDestinationTag()); + EXPECT_FALSE(tx.getDestinationTag().has_value()); + EXPECT_FALSE(tx.hasDeliverMin()); + EXPECT_FALSE(tx.getDeliverMin().has_value()); + EXPECT_FALSE(tx.hasCredentialIDs()); + EXPECT_FALSE(tx.getCredentialIDs().has_value()); + EXPECT_FALSE(tx.hasDomainID()); + EXPECT_FALSE(tx.getDomainID().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/PermissionedDomainDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/PermissionedDomainDeleteTests.cpp new file mode 100644 index 0000000000..e9f92fb5b3 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/PermissionedDomainDeleteTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction PermissionedDomainDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsPermissionedDomainDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPermissionedDomainDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const domainIDValue = canonical_UINT256(); + + PermissionedDomainDeleteBuilder builder{ + accountValue, + domainIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = domainIDValue; + auto const actual = tx.getDomainID(); + expectEqualField(expected, actual, "sfDomainID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsPermissionedDomainDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPermissionedDomainDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const domainIDValue = canonical_UINT256(); + + // Build an initial transaction + PermissionedDomainDeleteBuilder initialBuilder{ + accountValue, + domainIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + PermissionedDomainDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = domainIDValue; + auto const actual = rebuiltTx.getDomainID(); + expectEqualField(expected, actual, "sfDomainID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsPermissionedDomainDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PermissionedDomainDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsPermissionedDomainDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PermissionedDomainDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/PermissionedDomainSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/PermissionedDomainSetTests.cpp new file mode 100644 index 0000000000..eeec170f4c --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/PermissionedDomainSetTests.cpp @@ -0,0 +1,195 @@ +// Auto-generated unit tests for transaction PermissionedDomainSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsPermissionedDomainSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPermissionedDomainSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const domainIDValue = canonical_UINT256(); + auto const acceptedCredentialsValue = canonical_ARRAY(); + + PermissionedDomainSetBuilder builder{ + accountValue, + acceptedCredentialsValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDomainID(domainIDValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = acceptedCredentialsValue; + auto const actual = tx.getAcceptedCredentials(); + expectEqualField(expected, actual, "sfAcceptedCredentials"); + } + + // Verify optional fields + { + auto const& expected = domainIDValue; + auto const actualOpt = tx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(tx.hasDomainID()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsPermissionedDomainSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPermissionedDomainSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const domainIDValue = canonical_UINT256(); + auto const acceptedCredentialsValue = canonical_ARRAY(); + + // Build an initial transaction + PermissionedDomainSetBuilder initialBuilder{ + accountValue, + acceptedCredentialsValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDomainID(domainIDValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + PermissionedDomainSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = acceptedCredentialsValue; + auto const actual = rebuiltTx.getAcceptedCredentials(); + expectEqualField(expected, actual, "sfAcceptedCredentials"); + } + + // Verify optional fields + { + auto const& expected = domainIDValue; + auto const actualOpt = rebuiltTx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsPermissionedDomainSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PermissionedDomainSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsPermissionedDomainSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PermissionedDomainSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsPermissionedDomainSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPermissionedDomainSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const acceptedCredentialsValue = canonical_ARRAY(); + + PermissionedDomainSetBuilder builder{ + accountValue, + acceptedCredentialsValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasDomainID()); + EXPECT_FALSE(tx.getDomainID().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/SetFeeTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/SetFeeTests.cpp new file mode 100644 index 0000000000..981bf72ea7 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/SetFeeTests.cpp @@ -0,0 +1,324 @@ +// Auto-generated unit tests for transaction SetFee + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsSetFeeTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSetFee")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ledgerSequenceValue = canonical_UINT32(); + auto const baseFeeValue = canonical_UINT64(); + auto const referenceFeeUnitsValue = canonical_UINT32(); + auto const reserveBaseValue = canonical_UINT32(); + auto const reserveIncrementValue = canonical_UINT32(); + auto const baseFeeDropsValue = canonical_AMOUNT(); + auto const reserveBaseDropsValue = canonical_AMOUNT(); + auto const reserveIncrementDropsValue = canonical_AMOUNT(); + + SetFeeBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setLedgerSequence(ledgerSequenceValue); + builder.setBaseFee(baseFeeValue); + builder.setReferenceFeeUnits(referenceFeeUnitsValue); + builder.setReserveBase(reserveBaseValue); + builder.setReserveIncrement(reserveIncrementValue); + builder.setBaseFeeDrops(baseFeeDropsValue); + builder.setReserveBaseDrops(reserveBaseDropsValue); + builder.setReserveIncrementDrops(reserveIncrementDropsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = ledgerSequenceValue; + auto const actualOpt = tx.getLedgerSequence(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLedgerSequence should be present"; + expectEqualField(expected, *actualOpt, "sfLedgerSequence"); + EXPECT_TRUE(tx.hasLedgerSequence()); + } + + { + auto const& expected = baseFeeValue; + auto const actualOpt = tx.getBaseFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBaseFee should be present"; + expectEqualField(expected, *actualOpt, "sfBaseFee"); + EXPECT_TRUE(tx.hasBaseFee()); + } + + { + auto const& expected = referenceFeeUnitsValue; + auto const actualOpt = tx.getReferenceFeeUnits(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReferenceFeeUnits should be present"; + expectEqualField(expected, *actualOpt, "sfReferenceFeeUnits"); + EXPECT_TRUE(tx.hasReferenceFeeUnits()); + } + + { + auto const& expected = reserveBaseValue; + auto const actualOpt = tx.getReserveBase(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveBase should be present"; + expectEqualField(expected, *actualOpt, "sfReserveBase"); + EXPECT_TRUE(tx.hasReserveBase()); + } + + { + auto const& expected = reserveIncrementValue; + auto const actualOpt = tx.getReserveIncrement(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveIncrement should be present"; + expectEqualField(expected, *actualOpt, "sfReserveIncrement"); + EXPECT_TRUE(tx.hasReserveIncrement()); + } + + { + auto const& expected = baseFeeDropsValue; + auto const actualOpt = tx.getBaseFeeDrops(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBaseFeeDrops should be present"; + expectEqualField(expected, *actualOpt, "sfBaseFeeDrops"); + EXPECT_TRUE(tx.hasBaseFeeDrops()); + } + + { + auto const& expected = reserveBaseDropsValue; + auto const actualOpt = tx.getReserveBaseDrops(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveBaseDrops should be present"; + expectEqualField(expected, *actualOpt, "sfReserveBaseDrops"); + EXPECT_TRUE(tx.hasReserveBaseDrops()); + } + + { + auto const& expected = reserveIncrementDropsValue; + auto const actualOpt = tx.getReserveIncrementDrops(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveIncrementDrops should be present"; + expectEqualField(expected, *actualOpt, "sfReserveIncrementDrops"); + EXPECT_TRUE(tx.hasReserveIncrementDrops()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsSetFeeTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSetFeeFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ledgerSequenceValue = canonical_UINT32(); + auto const baseFeeValue = canonical_UINT64(); + auto const referenceFeeUnitsValue = canonical_UINT32(); + auto const reserveBaseValue = canonical_UINT32(); + auto const reserveIncrementValue = canonical_UINT32(); + auto const baseFeeDropsValue = canonical_AMOUNT(); + auto const reserveBaseDropsValue = canonical_AMOUNT(); + auto const reserveIncrementDropsValue = canonical_AMOUNT(); + + // Build an initial transaction + SetFeeBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setLedgerSequence(ledgerSequenceValue); + initialBuilder.setBaseFee(baseFeeValue); + initialBuilder.setReferenceFeeUnits(referenceFeeUnitsValue); + initialBuilder.setReserveBase(reserveBaseValue); + initialBuilder.setReserveIncrement(reserveIncrementValue); + initialBuilder.setBaseFeeDrops(baseFeeDropsValue); + initialBuilder.setReserveBaseDrops(reserveBaseDropsValue); + initialBuilder.setReserveIncrementDrops(reserveIncrementDropsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + SetFeeBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = ledgerSequenceValue; + auto const actualOpt = rebuiltTx.getLedgerSequence(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLedgerSequence should be present"; + expectEqualField(expected, *actualOpt, "sfLedgerSequence"); + } + + { + auto const& expected = baseFeeValue; + auto const actualOpt = rebuiltTx.getBaseFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBaseFee should be present"; + expectEqualField(expected, *actualOpt, "sfBaseFee"); + } + + { + auto const& expected = referenceFeeUnitsValue; + auto const actualOpt = rebuiltTx.getReferenceFeeUnits(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReferenceFeeUnits should be present"; + expectEqualField(expected, *actualOpt, "sfReferenceFeeUnits"); + } + + { + auto const& expected = reserveBaseValue; + auto const actualOpt = rebuiltTx.getReserveBase(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveBase should be present"; + expectEqualField(expected, *actualOpt, "sfReserveBase"); + } + + { + auto const& expected = reserveIncrementValue; + auto const actualOpt = rebuiltTx.getReserveIncrement(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveIncrement should be present"; + expectEqualField(expected, *actualOpt, "sfReserveIncrement"); + } + + { + auto const& expected = baseFeeDropsValue; + auto const actualOpt = rebuiltTx.getBaseFeeDrops(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBaseFeeDrops should be present"; + expectEqualField(expected, *actualOpt, "sfBaseFeeDrops"); + } + + { + auto const& expected = reserveBaseDropsValue; + auto const actualOpt = rebuiltTx.getReserveBaseDrops(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveBaseDrops should be present"; + expectEqualField(expected, *actualOpt, "sfReserveBaseDrops"); + } + + { + auto const& expected = reserveIncrementDropsValue; + auto const actualOpt = rebuiltTx.getReserveIncrementDrops(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveIncrementDrops should be present"; + expectEqualField(expected, *actualOpt, "sfReserveIncrementDrops"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsSetFeeTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SetFee{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsSetFeeTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SetFeeBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsSetFeeTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSetFeeNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + SetFeeBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasLedgerSequence()); + EXPECT_FALSE(tx.getLedgerSequence().has_value()); + EXPECT_FALSE(tx.hasBaseFee()); + EXPECT_FALSE(tx.getBaseFee().has_value()); + EXPECT_FALSE(tx.hasReferenceFeeUnits()); + EXPECT_FALSE(tx.getReferenceFeeUnits().has_value()); + EXPECT_FALSE(tx.hasReserveBase()); + EXPECT_FALSE(tx.getReserveBase().has_value()); + EXPECT_FALSE(tx.hasReserveIncrement()); + EXPECT_FALSE(tx.getReserveIncrement().has_value()); + EXPECT_FALSE(tx.hasBaseFeeDrops()); + EXPECT_FALSE(tx.getBaseFeeDrops().has_value()); + EXPECT_FALSE(tx.hasReserveBaseDrops()); + EXPECT_FALSE(tx.getReserveBaseDrops().has_value()); + EXPECT_FALSE(tx.hasReserveIncrementDrops()); + EXPECT_FALSE(tx.getReserveIncrementDrops().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/SetRegularKeyTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/SetRegularKeyTests.cpp new file mode 100644 index 0000000000..ce4f8ce2ef --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/SetRegularKeyTests.cpp @@ -0,0 +1,177 @@ +// Auto-generated unit tests for transaction SetRegularKey + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsSetRegularKeyTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSetRegularKey")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const regularKeyValue = canonical_ACCOUNT(); + + SetRegularKeyBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setRegularKey(regularKeyValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = regularKeyValue; + auto const actualOpt = tx.getRegularKey(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfRegularKey should be present"; + expectEqualField(expected, *actualOpt, "sfRegularKey"); + EXPECT_TRUE(tx.hasRegularKey()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsSetRegularKeyTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSetRegularKeyFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const regularKeyValue = canonical_ACCOUNT(); + + // Build an initial transaction + SetRegularKeyBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setRegularKey(regularKeyValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + SetRegularKeyBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = regularKeyValue; + auto const actualOpt = rebuiltTx.getRegularKey(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfRegularKey should be present"; + expectEqualField(expected, *actualOpt, "sfRegularKey"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsSetRegularKeyTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SetRegularKey{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsSetRegularKeyTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SetRegularKeyBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsSetRegularKeyTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSetRegularKeyNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + SetRegularKeyBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasRegularKey()); + EXPECT_FALSE(tx.getRegularKey().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/SignerListSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/SignerListSetTests.cpp new file mode 100644 index 0000000000..8d027823d0 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/SignerListSetTests.cpp @@ -0,0 +1,195 @@ +// Auto-generated unit tests for transaction SignerListSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsSignerListSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSignerListSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const signerQuorumValue = canonical_UINT32(); + auto const signerEntriesValue = canonical_ARRAY(); + + SignerListSetBuilder builder{ + accountValue, + signerQuorumValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setSignerEntries(signerEntriesValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = signerQuorumValue; + auto const actual = tx.getSignerQuorum(); + expectEqualField(expected, actual, "sfSignerQuorum"); + } + + // Verify optional fields + { + auto const& expected = signerEntriesValue; + auto const actualOpt = tx.getSignerEntries(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSignerEntries should be present"; + expectEqualField(expected, *actualOpt, "sfSignerEntries"); + EXPECT_TRUE(tx.hasSignerEntries()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsSignerListSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSignerListSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const signerQuorumValue = canonical_UINT32(); + auto const signerEntriesValue = canonical_ARRAY(); + + // Build an initial transaction + SignerListSetBuilder initialBuilder{ + accountValue, + signerQuorumValue, + sequenceValue, + feeValue + }; + + initialBuilder.setSignerEntries(signerEntriesValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + SignerListSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = signerQuorumValue; + auto const actual = rebuiltTx.getSignerQuorum(); + expectEqualField(expected, actual, "sfSignerQuorum"); + } + + // Verify optional fields + { + auto const& expected = signerEntriesValue; + auto const actualOpt = rebuiltTx.getSignerEntries(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSignerEntries should be present"; + expectEqualField(expected, *actualOpt, "sfSignerEntries"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsSignerListSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SignerListSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsSignerListSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SignerListSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsSignerListSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSignerListSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const signerQuorumValue = canonical_UINT32(); + + SignerListSetBuilder builder{ + accountValue, + signerQuorumValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasSignerEntries()); + EXPECT_FALSE(tx.getSignerEntries().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/TicketCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/TicketCreateTests.cpp new file mode 100644 index 0000000000..bb07cdef84 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/TicketCreateTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction TicketCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsTicketCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testTicketCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ticketCountValue = canonical_UINT32(); + + TicketCreateBuilder builder{ + accountValue, + ticketCountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ticketCountValue; + auto const actual = tx.getTicketCount(); + expectEqualField(expected, actual, "sfTicketCount"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsTicketCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testTicketCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ticketCountValue = canonical_UINT32(); + + // Build an initial transaction + TicketCreateBuilder initialBuilder{ + accountValue, + ticketCountValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + TicketCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ticketCountValue; + auto const actual = rebuiltTx.getTicketCount(); + expectEqualField(expected, actual, "sfTicketCount"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsTicketCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(TicketCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsTicketCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(TicketCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/TrustSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/TrustSetTests.cpp new file mode 100644 index 0000000000..12b243ca5c --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/TrustSetTests.cpp @@ -0,0 +1,219 @@ +// Auto-generated unit tests for transaction TrustSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsTrustSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testTrustSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const limitAmountValue = canonical_AMOUNT(); + auto const qualityInValue = canonical_UINT32(); + auto const qualityOutValue = canonical_UINT32(); + + TrustSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setLimitAmount(limitAmountValue); + builder.setQualityIn(qualityInValue); + builder.setQualityOut(qualityOutValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = limitAmountValue; + auto const actualOpt = tx.getLimitAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLimitAmount should be present"; + expectEqualField(expected, *actualOpt, "sfLimitAmount"); + EXPECT_TRUE(tx.hasLimitAmount()); + } + + { + auto const& expected = qualityInValue; + auto const actualOpt = tx.getQualityIn(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfQualityIn should be present"; + expectEqualField(expected, *actualOpt, "sfQualityIn"); + EXPECT_TRUE(tx.hasQualityIn()); + } + + { + auto const& expected = qualityOutValue; + auto const actualOpt = tx.getQualityOut(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfQualityOut should be present"; + expectEqualField(expected, *actualOpt, "sfQualityOut"); + EXPECT_TRUE(tx.hasQualityOut()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsTrustSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testTrustSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const limitAmountValue = canonical_AMOUNT(); + auto const qualityInValue = canonical_UINT32(); + auto const qualityOutValue = canonical_UINT32(); + + // Build an initial transaction + TrustSetBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setLimitAmount(limitAmountValue); + initialBuilder.setQualityIn(qualityInValue); + initialBuilder.setQualityOut(qualityOutValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + TrustSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = limitAmountValue; + auto const actualOpt = rebuiltTx.getLimitAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLimitAmount should be present"; + expectEqualField(expected, *actualOpt, "sfLimitAmount"); + } + + { + auto const& expected = qualityInValue; + auto const actualOpt = rebuiltTx.getQualityIn(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfQualityIn should be present"; + expectEqualField(expected, *actualOpt, "sfQualityIn"); + } + + { + auto const& expected = qualityOutValue; + auto const actualOpt = rebuiltTx.getQualityOut(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfQualityOut should be present"; + expectEqualField(expected, *actualOpt, "sfQualityOut"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsTrustSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(TrustSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsTrustSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(TrustSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsTrustSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testTrustSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + TrustSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasLimitAmount()); + EXPECT_FALSE(tx.getLimitAmount().has_value()); + EXPECT_FALSE(tx.hasQualityIn()); + EXPECT_FALSE(tx.getQualityIn().has_value()); + EXPECT_FALSE(tx.hasQualityOut()); + EXPECT_FALSE(tx.getQualityOut().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/UNLModifyTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/UNLModifyTests.cpp new file mode 100644 index 0000000000..3481460641 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/UNLModifyTests.cpp @@ -0,0 +1,178 @@ +// Auto-generated unit tests for transaction UNLModify + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsUNLModifyTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testUNLModify")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const uNLModifyDisablingValue = canonical_UINT8(); + auto const ledgerSequenceValue = canonical_UINT32(); + auto const uNLModifyValidatorValue = canonical_VL(); + + UNLModifyBuilder builder{ + accountValue, + uNLModifyDisablingValue, + ledgerSequenceValue, + uNLModifyValidatorValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = uNLModifyDisablingValue; + auto const actual = tx.getUNLModifyDisabling(); + expectEqualField(expected, actual, "sfUNLModifyDisabling"); + } + + { + auto const& expected = ledgerSequenceValue; + auto const actual = tx.getLedgerSequence(); + expectEqualField(expected, actual, "sfLedgerSequence"); + } + + { + auto const& expected = uNLModifyValidatorValue; + auto const actual = tx.getUNLModifyValidator(); + expectEqualField(expected, actual, "sfUNLModifyValidator"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsUNLModifyTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testUNLModifyFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const uNLModifyDisablingValue = canonical_UINT8(); + auto const ledgerSequenceValue = canonical_UINT32(); + auto const uNLModifyValidatorValue = canonical_VL(); + + // Build an initial transaction + UNLModifyBuilder initialBuilder{ + accountValue, + uNLModifyDisablingValue, + ledgerSequenceValue, + uNLModifyValidatorValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + UNLModifyBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = uNLModifyDisablingValue; + auto const actual = rebuiltTx.getUNLModifyDisabling(); + expectEqualField(expected, actual, "sfUNLModifyDisabling"); + } + + { + auto const& expected = ledgerSequenceValue; + auto const actual = rebuiltTx.getLedgerSequence(); + expectEqualField(expected, actual, "sfLedgerSequence"); + } + + { + auto const& expected = uNLModifyValidatorValue; + auto const actual = rebuiltTx.getUNLModifyValidator(); + expectEqualField(expected, actual, "sfUNLModifyValidator"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsUNLModifyTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(UNLModify{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsUNLModifyTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(UNLModifyBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/VaultClawbackTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/VaultClawbackTests.cpp new file mode 100644 index 0000000000..d089e3d097 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/VaultClawbackTests.cpp @@ -0,0 +1,213 @@ +// Auto-generated unit tests for transaction VaultClawback + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsVaultClawbackTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultClawback")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const holderValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + + VaultClawbackBuilder builder{ + accountValue, + vaultIDValue, + holderValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAmount(amountValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = tx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + { + auto const& expected = holderValue; + auto const actual = tx.getHolder(); + expectEqualField(expected, actual, "sfHolder"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsVaultClawbackTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultClawbackFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const holderValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + + // Build an initial transaction + VaultClawbackBuilder initialBuilder{ + accountValue, + vaultIDValue, + holderValue, + sequenceValue, + feeValue + }; + + initialBuilder.setAmount(amountValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + VaultClawbackBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = rebuiltTx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + { + auto const& expected = holderValue; + auto const actual = rebuiltTx.getHolder(); + expectEqualField(expected, actual, "sfHolder"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsVaultClawbackTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultClawback{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsVaultClawbackTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultClawbackBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsVaultClawbackTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultClawbackNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const vaultIDValue = canonical_UINT256(); + auto const holderValue = canonical_ACCOUNT(); + + VaultClawbackBuilder builder{ + accountValue, + vaultIDValue, + holderValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/VaultCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/VaultCreateTests.cpp new file mode 100644 index 0000000000..da141ce812 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/VaultCreateTests.cpp @@ -0,0 +1,300 @@ +// Auto-generated unit tests for transaction VaultCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsVaultCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const assetsMaximumValue = canonical_NUMBER(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const domainIDValue = canonical_UINT256(); + auto const withdrawalPolicyValue = canonical_UINT8(); + auto const dataValue = canonical_VL(); + auto const scaleValue = canonical_UINT8(); + + VaultCreateBuilder builder{ + accountValue, + assetValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAssetsMaximum(assetsMaximumValue); + builder.setMPTokenMetadata(mPTokenMetadataValue); + builder.setDomainID(domainIDValue); + builder.setWithdrawalPolicy(withdrawalPolicyValue); + builder.setData(dataValue); + builder.setScale(scaleValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = tx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + // Verify optional fields + { + auto const& expected = assetsMaximumValue; + auto const actualOpt = tx.getAssetsMaximum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetsMaximum should be present"; + expectEqualField(expected, *actualOpt, "sfAssetsMaximum"); + EXPECT_TRUE(tx.hasAssetsMaximum()); + } + + { + auto const& expected = mPTokenMetadataValue; + auto const actualOpt = tx.getMPTokenMetadata(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMPTokenMetadata should be present"; + expectEqualField(expected, *actualOpt, "sfMPTokenMetadata"); + EXPECT_TRUE(tx.hasMPTokenMetadata()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = tx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(tx.hasDomainID()); + } + + { + auto const& expected = withdrawalPolicyValue; + auto const actualOpt = tx.getWithdrawalPolicy(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfWithdrawalPolicy should be present"; + expectEqualField(expected, *actualOpt, "sfWithdrawalPolicy"); + EXPECT_TRUE(tx.hasWithdrawalPolicy()); + } + + { + auto const& expected = dataValue; + auto const actualOpt = tx.getData(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present"; + expectEqualField(expected, *actualOpt, "sfData"); + EXPECT_TRUE(tx.hasData()); + } + + { + auto const& expected = scaleValue; + auto const actualOpt = tx.getScale(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfScale should be present"; + expectEqualField(expected, *actualOpt, "sfScale"); + EXPECT_TRUE(tx.hasScale()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsVaultCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const assetsMaximumValue = canonical_NUMBER(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const domainIDValue = canonical_UINT256(); + auto const withdrawalPolicyValue = canonical_UINT8(); + auto const dataValue = canonical_VL(); + auto const scaleValue = canonical_UINT8(); + + // Build an initial transaction + VaultCreateBuilder initialBuilder{ + accountValue, + assetValue, + sequenceValue, + feeValue + }; + + initialBuilder.setAssetsMaximum(assetsMaximumValue); + initialBuilder.setMPTokenMetadata(mPTokenMetadataValue); + initialBuilder.setDomainID(domainIDValue); + initialBuilder.setWithdrawalPolicy(withdrawalPolicyValue); + initialBuilder.setData(dataValue); + initialBuilder.setScale(scaleValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + VaultCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = rebuiltTx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + // Verify optional fields + { + auto const& expected = assetsMaximumValue; + auto const actualOpt = rebuiltTx.getAssetsMaximum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetsMaximum should be present"; + expectEqualField(expected, *actualOpt, "sfAssetsMaximum"); + } + + { + auto const& expected = mPTokenMetadataValue; + auto const actualOpt = rebuiltTx.getMPTokenMetadata(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMPTokenMetadata should be present"; + expectEqualField(expected, *actualOpt, "sfMPTokenMetadata"); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = rebuiltTx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + } + + { + auto const& expected = withdrawalPolicyValue; + auto const actualOpt = rebuiltTx.getWithdrawalPolicy(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfWithdrawalPolicy should be present"; + expectEqualField(expected, *actualOpt, "sfWithdrawalPolicy"); + } + + { + auto const& expected = dataValue; + auto const actualOpt = rebuiltTx.getData(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present"; + expectEqualField(expected, *actualOpt, "sfData"); + } + + { + auto const& expected = scaleValue; + auto const actualOpt = rebuiltTx.getScale(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfScale should be present"; + expectEqualField(expected, *actualOpt, "sfScale"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsVaultCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsVaultCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsVaultCreateTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultCreateNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const assetValue = canonical_ISSUE(); + + VaultCreateBuilder builder{ + accountValue, + assetValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAssetsMaximum()); + EXPECT_FALSE(tx.getAssetsMaximum().has_value()); + EXPECT_FALSE(tx.hasMPTokenMetadata()); + EXPECT_FALSE(tx.getMPTokenMetadata().has_value()); + EXPECT_FALSE(tx.hasDomainID()); + EXPECT_FALSE(tx.getDomainID().has_value()); + EXPECT_FALSE(tx.hasWithdrawalPolicy()); + EXPECT_FALSE(tx.getWithdrawalPolicy().has_value()); + EXPECT_FALSE(tx.hasData()); + EXPECT_FALSE(tx.getData().has_value()); + EXPECT_FALSE(tx.hasScale()); + EXPECT_FALSE(tx.getScale().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/VaultDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/VaultDeleteTests.cpp new file mode 100644 index 0000000000..24c89d249a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/VaultDeleteTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction VaultDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsVaultDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + + VaultDeleteBuilder builder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = tx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsVaultDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + + // Build an initial transaction + VaultDeleteBuilder initialBuilder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + VaultDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = rebuiltTx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsVaultDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsVaultDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/VaultDepositTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/VaultDepositTests.cpp new file mode 100644 index 0000000000..c18ee8bd62 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/VaultDepositTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction VaultDeposit + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsVaultDepositTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultDeposit")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + VaultDepositBuilder builder{ + accountValue, + vaultIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = tx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsVaultDepositTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultDepositFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + // Build an initial transaction + VaultDepositBuilder initialBuilder{ + accountValue, + vaultIDValue, + amountValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + VaultDepositBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = rebuiltTx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsVaultDepositTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultDeposit{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsVaultDepositTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultDepositBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/VaultSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/VaultSetTests.cpp new file mode 100644 index 0000000000..995c1019f3 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/VaultSetTests.cpp @@ -0,0 +1,237 @@ +// Auto-generated unit tests for transaction VaultSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsVaultSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const assetsMaximumValue = canonical_NUMBER(); + auto const domainIDValue = canonical_UINT256(); + auto const dataValue = canonical_VL(); + + VaultSetBuilder builder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAssetsMaximum(assetsMaximumValue); + builder.setDomainID(domainIDValue); + builder.setData(dataValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = tx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + // Verify optional fields + { + auto const& expected = assetsMaximumValue; + auto const actualOpt = tx.getAssetsMaximum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetsMaximum should be present"; + expectEqualField(expected, *actualOpt, "sfAssetsMaximum"); + EXPECT_TRUE(tx.hasAssetsMaximum()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = tx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(tx.hasDomainID()); + } + + { + auto const& expected = dataValue; + auto const actualOpt = tx.getData(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present"; + expectEqualField(expected, *actualOpt, "sfData"); + EXPECT_TRUE(tx.hasData()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsVaultSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const assetsMaximumValue = canonical_NUMBER(); + auto const domainIDValue = canonical_UINT256(); + auto const dataValue = canonical_VL(); + + // Build an initial transaction + VaultSetBuilder initialBuilder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setAssetsMaximum(assetsMaximumValue); + initialBuilder.setDomainID(domainIDValue); + initialBuilder.setData(dataValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + VaultSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = rebuiltTx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + // Verify optional fields + { + auto const& expected = assetsMaximumValue; + auto const actualOpt = rebuiltTx.getAssetsMaximum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetsMaximum should be present"; + expectEqualField(expected, *actualOpt, "sfAssetsMaximum"); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = rebuiltTx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + } + + { + auto const& expected = dataValue; + auto const actualOpt = rebuiltTx.getData(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present"; + expectEqualField(expected, *actualOpt, "sfData"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsVaultSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsVaultSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsVaultSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const vaultIDValue = canonical_UINT256(); + + VaultSetBuilder builder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAssetsMaximum()); + EXPECT_FALSE(tx.getAssetsMaximum().has_value()); + EXPECT_FALSE(tx.hasDomainID()); + EXPECT_FALSE(tx.getDomainID().has_value()); + EXPECT_FALSE(tx.hasData()); + EXPECT_FALSE(tx.getData().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/VaultWithdrawTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/VaultWithdrawTests.cpp new file mode 100644 index 0000000000..9d8a7fecb9 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/VaultWithdrawTests.cpp @@ -0,0 +1,234 @@ +// Auto-generated unit tests for transaction VaultWithdraw + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsVaultWithdrawTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultWithdraw")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + + VaultWithdrawBuilder builder{ + accountValue, + vaultIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDestination(destinationValue); + builder.setDestinationTag(destinationTagValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = tx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = tx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + EXPECT_TRUE(tx.hasDestination()); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = tx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + EXPECT_TRUE(tx.hasDestinationTag()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsVaultWithdrawTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultWithdrawFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + + // Build an initial transaction + VaultWithdrawBuilder initialBuilder{ + accountValue, + vaultIDValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDestination(destinationValue); + initialBuilder.setDestinationTag(destinationTagValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + VaultWithdrawBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = rebuiltTx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = rebuiltTx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = rebuiltTx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsVaultWithdrawTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultWithdraw{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsVaultWithdrawTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultWithdrawBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsVaultWithdrawTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultWithdrawNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const vaultIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + VaultWithdrawBuilder builder{ + accountValue, + vaultIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasDestination()); + EXPECT_FALSE(tx.getDestination().has_value()); + EXPECT_FALSE(tx.hasDestinationTag()); + EXPECT_FALSE(tx.getDestinationTag().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainAccountCreateCommitTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainAccountCreateCommitTests.cpp new file mode 100644 index 0000000000..c3f2f11a00 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainAccountCreateCommitTests.cpp @@ -0,0 +1,194 @@ +// Auto-generated unit tests for transaction XChainAccountCreateCommit + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainAccountCreateCommitTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainAccountCreateCommit")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const signatureRewardValue = canonical_AMOUNT(); + + XChainAccountCreateCommitBuilder builder{ + accountValue, + xChainBridgeValue, + destinationValue, + amountValue, + signatureRewardValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = tx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainAccountCreateCommitTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainAccountCreateCommitFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const signatureRewardValue = canonical_AMOUNT(); + + // Build an initial transaction + XChainAccountCreateCommitBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + destinationValue, + amountValue, + signatureRewardValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainAccountCreateCommitBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = rebuiltTx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainAccountCreateCommitTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainAccountCreateCommit{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainAccountCreateCommitTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainAccountCreateCommitBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestationTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestationTests.cpp new file mode 100644 index 0000000000..82f9c6afc1 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestationTests.cpp @@ -0,0 +1,306 @@ +// Auto-generated unit tests for transaction XChainAddAccountCreateAttestation + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainAddAccountCreateAttestationTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainAddAccountCreateAttestation")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const attestationSignerAccountValue = canonical_ACCOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const signatureValue = canonical_VL(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const attestationRewardAccountValue = canonical_ACCOUNT(); + auto const wasLockingChainSendValue = canonical_UINT8(); + auto const xChainAccountCreateCountValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + auto const signatureRewardValue = canonical_AMOUNT(); + + XChainAddAccountCreateAttestationBuilder builder{ + accountValue, + xChainBridgeValue, + attestationSignerAccountValue, + publicKeyValue, + signatureValue, + otherChainSourceValue, + amountValue, + attestationRewardAccountValue, + wasLockingChainSendValue, + xChainAccountCreateCountValue, + destinationValue, + signatureRewardValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = attestationSignerAccountValue; + auto const actual = tx.getAttestationSignerAccount(); + expectEqualField(expected, actual, "sfAttestationSignerAccount"); + } + + { + auto const& expected = publicKeyValue; + auto const actual = tx.getPublicKey(); + expectEqualField(expected, actual, "sfPublicKey"); + } + + { + auto const& expected = signatureValue; + auto const actual = tx.getSignature(); + expectEqualField(expected, actual, "sfSignature"); + } + + { + auto const& expected = otherChainSourceValue; + auto const actual = tx.getOtherChainSource(); + expectEqualField(expected, actual, "sfOtherChainSource"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = attestationRewardAccountValue; + auto const actual = tx.getAttestationRewardAccount(); + expectEqualField(expected, actual, "sfAttestationRewardAccount"); + } + + { + auto const& expected = wasLockingChainSendValue; + auto const actual = tx.getWasLockingChainSend(); + expectEqualField(expected, actual, "sfWasLockingChainSend"); + } + + { + auto const& expected = xChainAccountCreateCountValue; + auto const actual = tx.getXChainAccountCreateCount(); + expectEqualField(expected, actual, "sfXChainAccountCreateCount"); + } + + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = tx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainAddAccountCreateAttestationTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainAddAccountCreateAttestationFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const attestationSignerAccountValue = canonical_ACCOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const signatureValue = canonical_VL(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const attestationRewardAccountValue = canonical_ACCOUNT(); + auto const wasLockingChainSendValue = canonical_UINT8(); + auto const xChainAccountCreateCountValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + auto const signatureRewardValue = canonical_AMOUNT(); + + // Build an initial transaction + XChainAddAccountCreateAttestationBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + attestationSignerAccountValue, + publicKeyValue, + signatureValue, + otherChainSourceValue, + amountValue, + attestationRewardAccountValue, + wasLockingChainSendValue, + xChainAccountCreateCountValue, + destinationValue, + signatureRewardValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainAddAccountCreateAttestationBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = attestationSignerAccountValue; + auto const actual = rebuiltTx.getAttestationSignerAccount(); + expectEqualField(expected, actual, "sfAttestationSignerAccount"); + } + + { + auto const& expected = publicKeyValue; + auto const actual = rebuiltTx.getPublicKey(); + expectEqualField(expected, actual, "sfPublicKey"); + } + + { + auto const& expected = signatureValue; + auto const actual = rebuiltTx.getSignature(); + expectEqualField(expected, actual, "sfSignature"); + } + + { + auto const& expected = otherChainSourceValue; + auto const actual = rebuiltTx.getOtherChainSource(); + expectEqualField(expected, actual, "sfOtherChainSource"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = attestationRewardAccountValue; + auto const actual = rebuiltTx.getAttestationRewardAccount(); + expectEqualField(expected, actual, "sfAttestationRewardAccount"); + } + + { + auto const& expected = wasLockingChainSendValue; + auto const actual = rebuiltTx.getWasLockingChainSend(); + expectEqualField(expected, actual, "sfWasLockingChainSend"); + } + + { + auto const& expected = xChainAccountCreateCountValue; + auto const actual = rebuiltTx.getXChainAccountCreateCount(); + expectEqualField(expected, actual, "sfXChainAccountCreateCount"); + } + + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = rebuiltTx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainAddAccountCreateAttestationTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainAddAccountCreateAttestation{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainAddAccountCreateAttestationTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainAddAccountCreateAttestationBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainAddClaimAttestationTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainAddClaimAttestationTests.cpp new file mode 100644 index 0000000000..6b9144fa68 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainAddClaimAttestationTests.cpp @@ -0,0 +1,339 @@ +// Auto-generated unit tests for transaction XChainAddClaimAttestation + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainAddClaimAttestationTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainAddClaimAttestation")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const attestationSignerAccountValue = canonical_ACCOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const signatureValue = canonical_VL(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const attestationRewardAccountValue = canonical_ACCOUNT(); + auto const wasLockingChainSendValue = canonical_UINT8(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + + XChainAddClaimAttestationBuilder builder{ + accountValue, + xChainBridgeValue, + attestationSignerAccountValue, + publicKeyValue, + signatureValue, + otherChainSourceValue, + amountValue, + attestationRewardAccountValue, + wasLockingChainSendValue, + xChainClaimIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDestination(destinationValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = attestationSignerAccountValue; + auto const actual = tx.getAttestationSignerAccount(); + expectEqualField(expected, actual, "sfAttestationSignerAccount"); + } + + { + auto const& expected = publicKeyValue; + auto const actual = tx.getPublicKey(); + expectEqualField(expected, actual, "sfPublicKey"); + } + + { + auto const& expected = signatureValue; + auto const actual = tx.getSignature(); + expectEqualField(expected, actual, "sfSignature"); + } + + { + auto const& expected = otherChainSourceValue; + auto const actual = tx.getOtherChainSource(); + expectEqualField(expected, actual, "sfOtherChainSource"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = attestationRewardAccountValue; + auto const actual = tx.getAttestationRewardAccount(); + expectEqualField(expected, actual, "sfAttestationRewardAccount"); + } + + { + auto const& expected = wasLockingChainSendValue; + auto const actual = tx.getWasLockingChainSend(); + expectEqualField(expected, actual, "sfWasLockingChainSend"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = tx.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = tx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + EXPECT_TRUE(tx.hasDestination()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainAddClaimAttestationTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainAddClaimAttestationFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const attestationSignerAccountValue = canonical_ACCOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const signatureValue = canonical_VL(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const attestationRewardAccountValue = canonical_ACCOUNT(); + auto const wasLockingChainSendValue = canonical_UINT8(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + + // Build an initial transaction + XChainAddClaimAttestationBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + attestationSignerAccountValue, + publicKeyValue, + signatureValue, + otherChainSourceValue, + amountValue, + attestationRewardAccountValue, + wasLockingChainSendValue, + xChainClaimIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDestination(destinationValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainAddClaimAttestationBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = attestationSignerAccountValue; + auto const actual = rebuiltTx.getAttestationSignerAccount(); + expectEqualField(expected, actual, "sfAttestationSignerAccount"); + } + + { + auto const& expected = publicKeyValue; + auto const actual = rebuiltTx.getPublicKey(); + expectEqualField(expected, actual, "sfPublicKey"); + } + + { + auto const& expected = signatureValue; + auto const actual = rebuiltTx.getSignature(); + expectEqualField(expected, actual, "sfSignature"); + } + + { + auto const& expected = otherChainSourceValue; + auto const actual = rebuiltTx.getOtherChainSource(); + expectEqualField(expected, actual, "sfOtherChainSource"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = attestationRewardAccountValue; + auto const actual = rebuiltTx.getAttestationRewardAccount(); + expectEqualField(expected, actual, "sfAttestationRewardAccount"); + } + + { + auto const& expected = wasLockingChainSendValue; + auto const actual = rebuiltTx.getWasLockingChainSend(); + expectEqualField(expected, actual, "sfWasLockingChainSend"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = rebuiltTx.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = rebuiltTx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainAddClaimAttestationTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainAddClaimAttestation{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainAddClaimAttestationTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainAddClaimAttestationBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsXChainAddClaimAttestationTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainAddClaimAttestationNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const attestationSignerAccountValue = canonical_ACCOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const signatureValue = canonical_VL(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const attestationRewardAccountValue = canonical_ACCOUNT(); + auto const wasLockingChainSendValue = canonical_UINT8(); + auto const xChainClaimIDValue = canonical_UINT64(); + + XChainAddClaimAttestationBuilder builder{ + accountValue, + xChainBridgeValue, + attestationSignerAccountValue, + publicKeyValue, + signatureValue, + otherChainSourceValue, + amountValue, + attestationRewardAccountValue, + wasLockingChainSendValue, + xChainClaimIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasDestination()); + EXPECT_FALSE(tx.getDestination().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainClaimTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainClaimTests.cpp new file mode 100644 index 0000000000..69101c843d --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainClaimTests.cpp @@ -0,0 +1,249 @@ +// Auto-generated unit tests for transaction XChainClaim + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainClaimTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainClaim")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + auto const amountValue = canonical_AMOUNT(); + + XChainClaimBuilder builder{ + accountValue, + xChainBridgeValue, + xChainClaimIDValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDestinationTag(destinationTagValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = tx.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = destinationTagValue; + auto const actualOpt = tx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + EXPECT_TRUE(tx.hasDestinationTag()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainClaimTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainClaimFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + auto const amountValue = canonical_AMOUNT(); + + // Build an initial transaction + XChainClaimBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + xChainClaimIDValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDestinationTag(destinationTagValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainClaimBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = rebuiltTx.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = destinationTagValue; + auto const actualOpt = rebuiltTx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainClaimTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainClaim{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainClaimTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainClaimBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsXChainClaimTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainClaimNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + + XChainClaimBuilder builder{ + accountValue, + xChainBridgeValue, + xChainClaimIDValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasDestinationTag()); + EXPECT_FALSE(tx.getDestinationTag().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainCommitTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainCommitTests.cpp new file mode 100644 index 0000000000..e6c72ec908 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainCommitTests.cpp @@ -0,0 +1,231 @@ +// Auto-generated unit tests for transaction XChainCommit + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainCommitTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCommit")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const amountValue = canonical_AMOUNT(); + auto const otherChainDestinationValue = canonical_ACCOUNT(); + + XChainCommitBuilder builder{ + accountValue, + xChainBridgeValue, + xChainClaimIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setOtherChainDestination(otherChainDestinationValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = tx.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = otherChainDestinationValue; + auto const actualOpt = tx.getOtherChainDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOtherChainDestination should be present"; + expectEqualField(expected, *actualOpt, "sfOtherChainDestination"); + EXPECT_TRUE(tx.hasOtherChainDestination()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainCommitTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCommitFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const amountValue = canonical_AMOUNT(); + auto const otherChainDestinationValue = canonical_ACCOUNT(); + + // Build an initial transaction + XChainCommitBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + xChainClaimIDValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setOtherChainDestination(otherChainDestinationValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainCommitBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = rebuiltTx.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = otherChainDestinationValue; + auto const actualOpt = rebuiltTx.getOtherChainDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOtherChainDestination should be present"; + expectEqualField(expected, *actualOpt, "sfOtherChainDestination"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainCommitTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainCommit{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainCommitTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainCommitBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsXChainCommitTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCommitNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const amountValue = canonical_AMOUNT(); + + XChainCommitBuilder builder{ + accountValue, + xChainBridgeValue, + xChainClaimIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasOtherChainDestination()); + EXPECT_FALSE(tx.getOtherChainDestination().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainCreateBridgeTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainCreateBridgeTests.cpp new file mode 100644 index 0000000000..5f529440f3 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainCreateBridgeTests.cpp @@ -0,0 +1,213 @@ +// Auto-generated unit tests for transaction XChainCreateBridge + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainCreateBridgeTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCreateBridge")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const minAccountCreateAmountValue = canonical_AMOUNT(); + + XChainCreateBridgeBuilder builder{ + accountValue, + xChainBridgeValue, + signatureRewardValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setMinAccountCreateAmount(minAccountCreateAmountValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = tx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + // Verify optional fields + { + auto const& expected = minAccountCreateAmountValue; + auto const actualOpt = tx.getMinAccountCreateAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMinAccountCreateAmount should be present"; + expectEqualField(expected, *actualOpt, "sfMinAccountCreateAmount"); + EXPECT_TRUE(tx.hasMinAccountCreateAmount()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainCreateBridgeTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCreateBridgeFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const minAccountCreateAmountValue = canonical_AMOUNT(); + + // Build an initial transaction + XChainCreateBridgeBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + signatureRewardValue, + sequenceValue, + feeValue + }; + + initialBuilder.setMinAccountCreateAmount(minAccountCreateAmountValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainCreateBridgeBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = rebuiltTx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + // Verify optional fields + { + auto const& expected = minAccountCreateAmountValue; + auto const actualOpt = rebuiltTx.getMinAccountCreateAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMinAccountCreateAmount should be present"; + expectEqualField(expected, *actualOpt, "sfMinAccountCreateAmount"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainCreateBridgeTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainCreateBridge{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainCreateBridgeTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainCreateBridgeBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsXChainCreateBridgeTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCreateBridgeNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const signatureRewardValue = canonical_AMOUNT(); + + XChainCreateBridgeBuilder builder{ + accountValue, + xChainBridgeValue, + signatureRewardValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasMinAccountCreateAmount()); + EXPECT_FALSE(tx.getMinAccountCreateAmount().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainCreateClaimIDTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainCreateClaimIDTests.cpp new file mode 100644 index 0000000000..41c71b9d8d --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainCreateClaimIDTests.cpp @@ -0,0 +1,178 @@ +// Auto-generated unit tests for transaction XChainCreateClaimID + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainCreateClaimIDTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCreateClaimID")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + + XChainCreateClaimIDBuilder builder{ + accountValue, + xChainBridgeValue, + signatureRewardValue, + otherChainSourceValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = tx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + { + auto const& expected = otherChainSourceValue; + auto const actual = tx.getOtherChainSource(); + expectEqualField(expected, actual, "sfOtherChainSource"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainCreateClaimIDTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCreateClaimIDFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + + // Build an initial transaction + XChainCreateClaimIDBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + signatureRewardValue, + otherChainSourceValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainCreateClaimIDBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = rebuiltTx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + { + auto const& expected = otherChainSourceValue; + auto const actual = rebuiltTx.getOtherChainSource(); + expectEqualField(expected, actual, "sfOtherChainSource"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainCreateClaimIDTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainCreateClaimID{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainCreateClaimIDTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainCreateClaimIDBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainModifyBridgeTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainModifyBridgeTests.cpp new file mode 100644 index 0000000000..7b3c4be5ce --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainModifyBridgeTests.cpp @@ -0,0 +1,216 @@ +// Auto-generated unit tests for transaction XChainModifyBridge + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainModifyBridgeTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainModifyBridge")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const minAccountCreateAmountValue = canonical_AMOUNT(); + + XChainModifyBridgeBuilder builder{ + accountValue, + xChainBridgeValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setSignatureReward(signatureRewardValue); + builder.setMinAccountCreateAmount(minAccountCreateAmountValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + // Verify optional fields + { + auto const& expected = signatureRewardValue; + auto const actualOpt = tx.getSignatureReward(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSignatureReward should be present"; + expectEqualField(expected, *actualOpt, "sfSignatureReward"); + EXPECT_TRUE(tx.hasSignatureReward()); + } + + { + auto const& expected = minAccountCreateAmountValue; + auto const actualOpt = tx.getMinAccountCreateAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMinAccountCreateAmount should be present"; + expectEqualField(expected, *actualOpt, "sfMinAccountCreateAmount"); + EXPECT_TRUE(tx.hasMinAccountCreateAmount()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainModifyBridgeTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainModifyBridgeFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const minAccountCreateAmountValue = canonical_AMOUNT(); + + // Build an initial transaction + XChainModifyBridgeBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + sequenceValue, + feeValue + }; + + initialBuilder.setSignatureReward(signatureRewardValue); + initialBuilder.setMinAccountCreateAmount(minAccountCreateAmountValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainModifyBridgeBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + // Verify optional fields + { + auto const& expected = signatureRewardValue; + auto const actualOpt = rebuiltTx.getSignatureReward(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSignatureReward should be present"; + expectEqualField(expected, *actualOpt, "sfSignatureReward"); + } + + { + auto const& expected = minAccountCreateAmountValue; + auto const actualOpt = rebuiltTx.getMinAccountCreateAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMinAccountCreateAmount should be present"; + expectEqualField(expected, *actualOpt, "sfMinAccountCreateAmount"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainModifyBridgeTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainModifyBridge{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainModifyBridgeTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainModifyBridgeBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsXChainModifyBridgeTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainModifyBridgeNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + + XChainModifyBridgeBuilder builder{ + accountValue, + xChainBridgeValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasSignatureReward()); + EXPECT_FALSE(tx.getSignatureReward().has_value()); + EXPECT_FALSE(tx.hasMinAccountCreateAmount()); + EXPECT_FALSE(tx.getMinAccountCreateAmount().has_value()); +} + +} diff --git a/src/xrpld/README.md b/src/xrpld/README.md index cf71589dd7..83dc8c6a84 100644 --- a/src/xrpld/README.md +++ b/src/xrpld/README.md @@ -1,4 +1,4 @@ -# Ripple Source Guidelines +# XRPL Source Guidelines Each folder contains a single module following the newest style: diff --git a/src/xrpld/app/consensus/RCLConsensus.cpp b/src/xrpld/app/consensus/RCLConsensus.cpp index 91f24a650c..b7b0919aad 100644 --- a/src/xrpld/app/consensus/RCLConsensus.cpp +++ b/src/xrpld/app/consensus/RCLConsensus.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -11,7 +10,6 @@ #include #include #include -#include #include #include @@ -20,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -137,7 +137,7 @@ RCLConsensus::Adaptor::share(RCLCxPeerPos const& peerPos) prop.set_closetime(proposal.closeTime().time_since_epoch().count()); prop.set_currenttxhash(proposal.position().begin(), proposal.position().size()); - prop.set_previousledger(proposal.prevLedger().begin(), proposal.position().size()); + prop.set_previousledger(proposal.prevLedger().begin(), proposal.prevLedger().size()); auto const pk = peerPos.publicKey().slice(); prop.set_nodepubkey(pk.data(), pk.size()); @@ -145,7 +145,7 @@ RCLConsensus::Adaptor::share(RCLCxPeerPos const& peerPos) auto const sig = peerPos.signature(); prop.set_signature(sig.data(), sig.size()); - app_.overlay().relay(prop, peerPos.suppressionID(), peerPos.publicKey()); + app_.getOverlay().relay(prop, peerPos.suppressionID(), peerPos.publicKey()); } void @@ -159,9 +159,9 @@ RCLConsensus::Adaptor::share(RCLCxTx const& tx) protocol::TMTransaction msg; msg.set_rawtransaction(slice.data(), slice.size()); msg.set_status(protocol::tsNEW); - msg.set_receivetimestamp(app_.timeKeeper().now().time_since_epoch().count()); - static std::set skip{}; - app_.overlay().relay(tx.id(), msg, skip); + msg.set_receivetimestamp(app_.getTimeKeeper().now().time_since_epoch().count()); + static std::set const skip{}; + app_.getOverlay().relay(tx.id(), msg, skip); } else { @@ -207,7 +207,7 @@ RCLConsensus::Adaptor::propose(RCLCxPeerPos::Proposal const& proposal) app_.getHashRouter().addSuppression(suppression); - app_.overlay().broadcast(prop); + app_.getOverlay().broadcast(prop); } void @@ -229,7 +229,7 @@ RCLConsensus::Adaptor::acquireTxSet(RCLTxSet::ID const& setId) bool RCLConsensus::Adaptor::hasOpenTransactions() const { - return !app_.openLedger().empty(); + return !app_.getOpenLedger().empty(); } std::size_t @@ -284,7 +284,7 @@ RCLConsensus::Adaptor::onClose( // Tell the ledger master not to acquire the ledger we're probably building ledgerMaster_.setBuildingLedger(prevLedger->header().seq + 1); - auto initialLedger = app_.openLedger().current(); + auto initialLedger = app_.getOpenLedger().current(); auto initialSet = std::make_shared(SHAMapType::TRANSACTION, app_.getNodeFamily()); initialSet->setUnbacked(); @@ -308,9 +308,9 @@ RCLConsensus::Adaptor::onClose( // previous ledger was flag ledger, add fee and amendment // pseudo-transactions auto validations = - app_.validators().negativeUNLFilter(app_.getValidations().getTrustedForLedger( + app_.getValidators().negativeUNLFilter(app_.getValidations().getTrustedForLedger( prevLedger->header().parentHash, prevLedger->seq() - 1)); - if (validations.size() >= app_.validators().quorum()) + if (validations.size() >= app_.getValidators().quorum()) { feeVote_->doVoting(prevLedger, validations, initialSet); app_.getAmendmentTable().doVoting(prevLedger, validations, initialSet, j_); @@ -323,7 +323,7 @@ RCLConsensus::Adaptor::onClose( // add negative UNL pseudo-transactions nUnlVote_.doVoting( prevLedger, - app_.validators().getTrustedMasterKeys(), + app_.getValidators().getTrustedMasterKeys(), app_.getValidations(), initialSet); } @@ -355,7 +355,7 @@ RCLConsensus::Adaptor::onClose( RCLCxPeerPos::Proposal::seqJoin, setHash, closeTime, - app_.timeKeeper().closeTime(), + app_.getTimeKeeper().closeTime(), validatorKeys_.nodeID}}; } @@ -382,7 +382,10 @@ RCLConsensus::Adaptor::onAccept( bool const validating) { app_.getJobQueue().addJob( - jtACCEPT, "AcceptLedger", [=, this, cj = std::move(consensusJson)]() mutable { + jtACCEPT, + "AcceptLedger", + // NOLINTNEXTLINE(cppcoreguidelines-misleading-capture-default-by-value) + [=, this, cj = std::move(consensusJson)]() mutable { // Note that no lock is held or acquired during this job. // This is because generic Consensus guarantees that once a ledger // is accepted, the consensus results and capture by reference state @@ -406,7 +409,7 @@ RCLConsensus::Adaptor::doAccept( prevProposers_ = result.proposers; prevRoundTime_ = result.roundTime.read(); - bool closeTimeCorrect; + bool closeTimeCorrect = false; bool const proposing = mode == ConsensusMode::proposing; bool const haveCorrectLCL = mode != ConsensusMode::wrongLedger; @@ -493,9 +496,9 @@ RCLConsensus::Adaptor::doAccept( censorshipDetector_.check( std::move(accepted), - [curr = built.seq(), j = app_.journal("CensorshipDetector"), &failed]( + [curr = built.seq(), j = app_.getJournal("CensorshipDetector"), &failed]( uint256 const& id, LedgerIndex seq) { - if (failed.count(id)) + if (failed.contains(id)) return true; auto const wait = curr - seq; @@ -582,10 +585,14 @@ RCLConsensus::Adaptor::doAccept( auto const lastVal = ledgerMaster_.getValidatedLedger(); std::optional rules; if (lastVal) + { rules = makeRulesGivenLedger(*lastVal, app_.config().features); + } else + { rules.emplace(app_.config().features); - app_.openLedger().accept( + } + app_.getOpenLedger().accept( app_, *rules, built.ledger_, @@ -613,7 +620,7 @@ RCLConsensus::Adaptor::doAccept( ledgerMaster_.getClosedLedger()->header().hash == built.id(), "xrpl::RCLConsensus::Adaptor::doAccept : ledger hash match"); XRPL_ASSERT( - app_.openLedger().current()->header().parentHash == built.id(), + app_.getOpenLedger().current()->header().parentHash == built.id(), "xrpl::RCLConsensus::Adaptor::doAccept : parent hash match"); } @@ -648,7 +655,7 @@ RCLConsensus::Adaptor::doAccept( JLOG(j_.info()) << "Our close offset is estimated at " << offset.count() << " (" << closeCount << ")"; - app_.timeKeeper().adjustCloseTime(offset); + app_.getTimeKeeper().adjustCloseTime(offset); } } @@ -661,17 +668,21 @@ RCLConsensus::Adaptor::notify( protocol::TMStatusChange s; if (!haveCorrectLCL) + { s.set_newevent(protocol::neLOST_SYNC); + } else + { s.set_newevent(ne); + } s.set_ledgerseq(ledger.seq()); - s.set_networktime(app_.timeKeeper().now().time_since_epoch().count()); + s.set_networktime(app_.getTimeKeeper().now().time_since_epoch().count()); s.set_ledgerhashprevious( ledger.parentID().begin(), std::decay_t::bytes); s.set_ledgerhash(ledger.id().begin(), std::decay_t::bytes); - std::uint32_t uMin, uMax; + std::uint32_t uMin = 0, uMax = 0; if (!ledgerMaster_.getFullValidatedRange(uMin, uMax)) { uMin = 0; @@ -684,7 +695,7 @@ RCLConsensus::Adaptor::notify( } s.set_firstseq(uMin); s.set_lastseq(uMax); - app_.overlay().foreach(send_always(std::make_shared(s, protocol::mtSTATUS_CHANGE))); + app_.getOverlay().foreach(send_always(std::make_shared(s, protocol::mtSTATUS_CHANGE))); JLOG(j_.trace()) << "send status change to peer"; } @@ -723,9 +734,13 @@ RCLConsensus::Adaptor::buildLCL( // And stash the ledger in the ledger master if (ledgerMaster_.storeLedger(built)) + { JLOG(j_.debug()) << "Consensus built ledger we already had"; + } else if (app_.getInboundLedgers().find(built->header().hash)) + { JLOG(j_.debug()) << "Consensus built ledger we were acquiring"; + } else JLOG(j_.debug()) << "Consensus built new ledger"; return RCLCxLedger{std::move(built)}; @@ -736,7 +751,7 @@ RCLConsensus::Adaptor::validate(RCLCxLedger const& ledger, RCLTxSet const& txns, { using namespace std::chrono_literals; - auto validationTime = app_.timeKeeper().closeTime(); + auto validationTime = app_.getTimeKeeper().closeTime(); if (validationTime <= lastValidationTime_) validationTime = lastValidationTime_ + 1s; lastValidationTime_ = validationTime; @@ -812,7 +827,7 @@ RCLConsensus::Adaptor::validate(RCLCxLedger const& ledger, RCLTxSet const& txns, // Broadcast to all our peers: protocol::TMValidation val; val.set_validation(serialized.data(), serialized.size()); - app_.overlay().broadcast(val); + app_.getOverlay().broadcast(val); // Publish to all our subscribers: app_.getOPs().pubValidation(v); @@ -838,7 +853,7 @@ RCLConsensus::getJson(bool full) const { Json::Value ret; { - std::lock_guard _{mutex_}; + std::lock_guard const _{mutex_}; ret = consensus_.getJson(full); } ret["validating"] = adaptor_.validating(); @@ -852,7 +867,7 @@ RCLConsensus::timerEntry( { try { - std::lock_guard _{mutex_}; + std::lock_guard const _{mutex_}; consensus_.timerEntry(now, clog); } catch (SHAMapMissingNode const& mn) @@ -871,7 +886,7 @@ RCLConsensus::gotTxSet(NetClock::time_point const& now, RCLTxSet const& txSet) { try { - std::lock_guard _{mutex_}; + std::lock_guard const _{mutex_}; consensus_.gotTxSet(now, txSet); } catch (SHAMapMissingNode const& mn) @@ -889,14 +904,14 @@ RCLConsensus::simulate( NetClock::time_point const& now, std::optional consensusDelay) { - std::lock_guard _{mutex_}; + std::lock_guard const _{mutex_}; consensus_.simulate(now, consensusDelay); } bool RCLConsensus::peerProposal(NetClock::time_point const& now, RCLCxPeerPos const& newProposal) { - std::lock_guard _{mutex_}; + std::lock_guard const _{mutex_}; return consensus_.peerProposal(now, newProposal); } @@ -910,11 +925,11 @@ RCLConsensus::Adaptor::preStartRound(RCLCxLedger const& prevLgr, hash_set> RCLConsensus::Adaptor::getQuorumKeys() const { - return app_.validators().getQuorumKeys(); + return app_.getValidators().getQuorumKeys(); } std::size_t @@ -982,7 +997,7 @@ RCLConsensus::Adaptor::validator() const void RCLConsensus::Adaptor::updateOperatingMode(std::size_t const positions) const { - if (!positions && app_.getOPs().isFull()) + if ((positions == 0u) && app_.getOPs().isFull()) app_.getOPs().setMode(OperatingMode::CONNECTED); } @@ -995,7 +1010,7 @@ RCLConsensus::startRound( hash_set const& nowTrusted, std::unique_ptr const& clog) { - std::lock_guard _{mutex_}; + std::lock_guard const _{mutex_}; consensus_.startRound( now, prevLgrId, prevLgr, nowUntrusted, adaptor_.preStartRound(prevLgr, nowTrusted), clog); } diff --git a/src/xrpld/app/consensus/RCLConsensus.h b/src/xrpld/app/consensus/RCLConsensus.h index 15d36a1aa6..c965ed3d87 100644 --- a/src/xrpld/app/consensus/RCLConsensus.h +++ b/src/xrpld/app/consensus/RCLConsensus.h @@ -472,7 +472,7 @@ public: RCLCxLedger::ID prevLedgerID() const { - std::lock_guard _{mutex_}; + std::lock_guard const _{mutex_}; return consensus_.prevLedgerID(); } diff --git a/src/xrpld/app/consensus/RCLCxLedger.h b/src/xrpld/app/consensus/RCLCxLedger.h index 6b96fc8af3..09111ebdb6 100644 --- a/src/xrpld/app/consensus/RCLCxLedger.h +++ b/src/xrpld/app/consensus/RCLCxLedger.h @@ -1,8 +1,8 @@ #pragma once -#include #include +#include #include #include diff --git a/src/xrpld/app/consensus/RCLCxPeerPos.cpp b/src/xrpld/app/consensus/RCLCxPeerPos.cpp index 4128d65c49..8f99dceea8 100644 --- a/src/xrpld/app/consensus/RCLCxPeerPos.cpp +++ b/src/xrpld/app/consensus/RCLCxPeerPos.cpp @@ -10,16 +10,16 @@ RCLCxPeerPos::RCLCxPeerPos( PublicKey const& publicKey, Slice const& signature, uint256 const& suppression, - Proposal&& proposal) - : publicKey_(publicKey), suppression_(suppression), proposal_(std::move(proposal)) + Proposal const& proposal) // trivially copyable + : publicKey_(publicKey), suppression_(suppression), proposal_(proposal) { // The maximum allowed size of a signature is 72 bytes; we verify // this elsewhere, but we want to be extra careful here: XRPL_ASSERT( - signature.size() != 0 && signature.size() <= signature_.capacity(), + !signature.empty() && signature.size() <= signature_.capacity(), "xrpl::RCLCxPeerPos::RCLCxPeerPos : valid signature size"); - if (signature.size() != 0 && signature.size() <= signature_.capacity()) + if (!signature.empty() && signature.size() <= signature_.capacity()) signature_.assign(signature.begin(), signature.end()); } @@ -34,7 +34,7 @@ RCLCxPeerPos::getJson() const { auto ret = proposal().getJson(); - if (publicKey().size()) + if (publicKey().size() != 0u) ret[jss::peer_id] = toBase58(TokenType::NodePublic, publicKey()); return ret; diff --git a/src/xrpld/app/consensus/RCLCxPeerPos.h b/src/xrpld/app/consensus/RCLCxPeerPos.h index 5dad4a33eb..e334320826 100644 --- a/src/xrpld/app/consensus/RCLCxPeerPos.h +++ b/src/xrpld/app/consensus/RCLCxPeerPos.h @@ -40,7 +40,7 @@ public: PublicKey const& publicKey, Slice const& signature, uint256 const& suppress, - Proposal&& proposal); + Proposal const& proposal); // trivially copyable //! Verify the signing hash of the proposal bool diff --git a/src/xrpld/app/consensus/RCLValidations.cpp b/src/xrpld/app/consensus/RCLValidations.cpp index 60e02f53b4..7bc16f194e 100644 --- a/src/xrpld/app/consensus/RCLValidations.cpp +++ b/src/xrpld/app/consensus/RCLValidations.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -100,7 +99,7 @@ RCLValidationsAdaptor::RCLValidationsAdaptor(Application& app, beast::Journal j) NetClock::time_point RCLValidationsAdaptor::now() const { - return app_.timeKeeper().closeTime(); + return app_.getTimeKeeper().closeTime(); } std::optional @@ -132,7 +131,7 @@ RCLValidationsAdaptor::acquire(LedgerHash const& hash) XRPL_ASSERT( ledger->header().hash == hash, "xrpl::RCLValidationsAdaptor::acquire : ledger hash match"); - return RCLValidatedLedger(std::move(ledger), j_); + return RCLValidatedLedger(ledger, j_); } void @@ -148,14 +147,14 @@ handleNewValidation( auto const seq = val->getFieldU32(sfLedgerSequence); // Ensure validation is marked as trusted if signer currently trusted - auto masterKey = app.validators().getTrustedKey(signingKey); + auto masterKey = app.getValidators().getTrustedKey(signingKey); if (!val->isTrusted() && masterKey) val->setTrusted(); // If not currently trusted, see if signer is currently listed if (!masterKey) - masterKey = app.validators().getListedKey(signingKey); + masterKey = app.getValidators().getListedKey(signingKey); auto& validations = app.getValidations(); @@ -205,14 +204,18 @@ handleNewValidation( }(); if (outcome == ValStatus::conflicting) + { ls << "Byzantine Behavior Detector: " << (val->isTrusted() ? "trusted " : "untrusted ") << id << ": Conflicting validation for " << seq << "!\n[" << val->getSerializer().slice() << "]"; + } if (outcome == ValStatus::multiple) + { ls << "Byzantine Behavior Detector: " << (val->isTrusted() ? "trusted " : "untrusted ") << id << ": Multiple validations for " << seq << "/" << hash << "!\n[" << val->getSerializer().slice() << "]"; + } } } diff --git a/src/xrpld/app/consensus/RCLValidations.h b/src/xrpld/app/consensus/RCLValidations.h index 35c1686f9e..8da82e6425 100644 --- a/src/xrpld/app/consensus/RCLValidations.h +++ b/src/xrpld/app/consensus/RCLValidations.h @@ -1,8 +1,8 @@ #pragma once -#include #include +#include #include #include #include diff --git a/src/xrpld/app/consensus/README.md b/src/xrpld/app/consensus/README.md index bdf5afe87c..44a4dc8ae7 100644 --- a/src/xrpld/app/consensus/README.md +++ b/src/xrpld/app/consensus/README.md @@ -2,11 +2,11 @@ This directory holds the types and classes needed to connect the generic consensus algorithm to the -rippled-specific instance of consensus. +xrpld-specific instance of consensus. - `RCLCxTx` adapts a `SHAMapItem` transaction. - `RCLCxTxSet` adapts a `SHAMap` to represent a set of transactions. - `RCLCxLedger` adapts a `Ledger`. - `RCLConsensus` is implements the requirements of the generic - `Consensus` class by connecting to the rest of the `rippled` + `Consensus` class by connecting to the rest of the `xrpld` application. diff --git a/src/xrpld/app/ledger/AcceptedLedger.cpp b/src/xrpld/app/ledger/AcceptedLedger.cpp index 339c5f57c7..1da70702bf 100644 --- a/src/xrpld/app/ledger/AcceptedLedger.cpp +++ b/src/xrpld/app/ledger/AcceptedLedger.cpp @@ -10,8 +10,10 @@ AcceptedLedger::AcceptedLedger(std::shared_ptr const& ledger) : auto insertAll = [&](auto const& txns) { for (auto const& item : txns) + { transactions_.emplace_back( std::make_unique(ledger, item.first, item.second)); + } }; transactions_.reserve(256); diff --git a/src/xrpld/app/ledger/ConsensusTransSetSF.cpp b/src/xrpld/app/ledger/ConsensusTransSetSF.cpp index 575a1a1460..5fd614a1d9 100644 --- a/src/xrpld/app/ledger/ConsensusTransSetSF.cpp +++ b/src/xrpld/app/ledger/ConsensusTransSetSF.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -12,7 +11,7 @@ namespace xrpl { ConsensusTransSetSF::ConsensusTransSetSF(Application& app, NodeCache& nodeCache) - : app_(app), m_nodeCache(nodeCache), j_(app.journal("TransactionAcquire")) + : app_(app), m_nodeCache(nodeCache), j_(app.getJournal("TransactionAcquire")) { } @@ -21,7 +20,7 @@ ConsensusTransSetSF::gotNode( bool fromFilter, SHAMapHash const& nodeHash, std::uint32_t, - Blob&& nodeData, + Blob&& nodeData, // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) SHAMapNodeType type) const { if (fromFilter) @@ -37,7 +36,7 @@ ConsensusTransSetSF::gotNode( try { // skip prefix - Serializer s(nodeData.data() + 4, nodeData.size() - 4); + Serializer const s(nodeData.data() + 4, nodeData.size() - 4); SerialIter sit(s.slice()); auto stx = std::make_shared(std::ref(sit)); XRPL_ASSERT( diff --git a/src/xrpld/app/ledger/InboundLedger.h b/src/xrpld/app/ledger/InboundLedger.h index ef1362365d..b17b59b27f 100644 --- a/src/xrpld/app/ledger/InboundLedger.h +++ b/src/xrpld/app/ledger/InboundLedger.h @@ -1,11 +1,11 @@ #pragma once -#include #include #include #include #include +#include #include #include @@ -152,11 +152,11 @@ private: clock_type::time_point mLastAction; std::shared_ptr mLedger; - bool mHaveHeader; - bool mHaveState; - bool mHaveTransactions; - bool mSignaled; - bool mByHash; + bool mHaveHeader{false}; + bool mHaveState{false}; + bool mHaveTransactions{false}; + bool mSignaled{false}; + bool mByHash{true}; std::uint32_t mSeq; Reason const mReason; @@ -168,7 +168,7 @@ private: std::mutex mReceivedDataLock; std::vector, std::shared_ptr>> mReceivedData; - bool mReceiveDispatched; + bool mReceiveDispatched{false}; std::unique_ptr mPeerSet; }; diff --git a/src/xrpld/app/ledger/LedgerHistory.cpp b/src/xrpld/app/ledger/LedgerHistory.cpp index f2e2afb3e6..969511db4c 100644 --- a/src/xrpld/app/ledger/LedgerHistory.cpp +++ b/src/xrpld/app/ledger/LedgerHistory.cpp @@ -1,7 +1,8 @@ #include +#include #include +#include -#include #include #include #include @@ -19,14 +20,14 @@ LedgerHistory::LedgerHistory(beast::insight::Collector::ptr const& collector, Ap app_.config().getValueFor(SizedItem::ledgerSize), std::chrono::seconds{app_.config().getValueFor(SizedItem::ledgerAge)}, stopwatch(), - app_.journal("TaggedCache")) + app_.getJournal("TaggedCache")) , m_consensus_validated( "ConsensusValidated", 64, std::chrono::minutes{5}, stopwatch(), - app_.journal("TaggedCache")) - , j_(app.journal("LedgerHistory")) + app_.getJournal("TaggedCache")) + , j_(app.getJournal("LedgerHistory")) { } @@ -39,7 +40,7 @@ LedgerHistory::insert(std::shared_ptr const& ledger, bool validate XRPL_ASSERT( ledger->stateMap().getHash().isNonZero(), "xrpl::LedgerHistory::insert : nonzero hash"); - std::unique_lock sl(m_ledgers_by_hash.peekMutex()); + std::unique_lock const sl(m_ledgers_by_hash.peekMutex()); bool const alreadyHad = m_ledgers_by_hash.canonicalize_replace_cache(ledger->header().hash, ledger); @@ -52,7 +53,7 @@ LedgerHistory::insert(std::shared_ptr const& ledger, bool validate LedgerHash LedgerHistory::getLedgerHash(LedgerIndex index) { - std::unique_lock sl(m_ledgers_by_hash.peekMutex()); + std::unique_lock const sl(m_ledgers_by_hash.peekMutex()); if (auto it = mLedgersByIndex.find(index); it != mLedgersByIndex.end()) return it->second; return {}; @@ -67,13 +68,15 @@ LedgerHistory::getLedgerBySeq(LedgerIndex index) if (it != mLedgersByIndex.end()) { - uint256 hash = it->second; + uint256 const hash = it->second; sl.unlock(); return getLedgerByHash(hash); } } - std::shared_ptr ret = loadByIndex(index, app_); + Rules const rules{app_.config().features}; + Fees const fees = app_.config().FEES.toFees(); + std::shared_ptr ret = loadByIndex(index, rules, fees, app_); if (!ret) return ret; @@ -83,7 +86,7 @@ LedgerHistory::getLedgerBySeq(LedgerIndex index) { // Add this ledger to the local tracking by index - std::unique_lock sl(m_ledgers_by_hash.peekMutex()); + std::unique_lock const sl(m_ledgers_by_hash.peekMutex()); XRPL_ASSERT( ret->isImmutable(), "xrpl::LedgerHistory::getLedgerBySeq : immutable result ledger"); @@ -111,7 +114,9 @@ LedgerHistory::getLedgerByHash(LedgerHash const& hash) return ret; } - ret = loadByHash(hash, app_); + Rules const rules{app_.config().features}; + Fees const fees = app_.config().FEES.toFees(); + ret = loadByHash(hash, rules, fees, app_); if (!ret) return ret; @@ -337,9 +342,11 @@ LedgerHistory::handleMismatch( if (builtConsensusHash && validatedConsensusHash) { if (builtConsensusHash != validatedConsensusHash) + { JLOG(j_.error()) << "MISMATCH on consensus transaction set " << " built: " << to_string(*builtConsensusHash) << " validated: " << to_string(*validatedConsensusHash); + } else JLOG(j_.error()) << "MISMATCH with same consensus transaction set: " << to_string(*builtConsensusHash); @@ -350,7 +357,9 @@ LedgerHistory::handleMismatch( auto const validTx = leaves(validLedger->txMap()); if (builtTx == validTx) + { JLOG(j_.error()) << "MISMATCH with same " << builtTx.size() << " transactions"; + } else JLOG(j_.error()) << "MISMATCH with " << builtTx.size() << " built and " << validTx.size() << " valid transactions."; @@ -396,11 +405,11 @@ LedgerHistory::builtLedger( uint256 const& consensusHash, Json::Value consensus) { - LedgerIndex index = ledger->header().seq; - LedgerHash hash = ledger->header().hash; + LedgerIndex const index = ledger->header().seq; + LedgerHash const hash = ledger->header().hash; XRPL_ASSERT(!hash.isZero(), "xrpl::LedgerHistory::builtLedger : nonzero hash"); - std::unique_lock sl(m_consensus_validated.peekMutex()); + std::unique_lock const sl(m_consensus_validated.peekMutex()); auto entry = std::make_shared(); m_consensus_validated.canonicalize_replace_client(index, entry); @@ -435,11 +444,11 @@ LedgerHistory::validatedLedger( std::shared_ptr const& ledger, std::optional const& consensusHash) { - LedgerIndex index = ledger->header().seq; - LedgerHash hash = ledger->header().hash; + LedgerIndex const index = ledger->header().seq; + LedgerHash const hash = ledger->header().hash; XRPL_ASSERT(!hash.isZero(), "xrpl::LedgerHistory::validatedLedger : nonzero hash"); - std::unique_lock sl(m_consensus_validated.peekMutex()); + std::unique_lock const sl(m_consensus_validated.peekMutex()); auto entry = std::make_shared(); m_consensus_validated.canonicalize_replace_client(index, entry); @@ -473,7 +482,7 @@ LedgerHistory::validatedLedger( bool LedgerHistory::fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash) { - std::unique_lock sl(m_ledgers_by_hash.peekMutex()); + std::unique_lock const sl(m_ledgers_by_hash.peekMutex()); auto it = mLedgersByIndex.find(ledgerIndex); if ((it != mLedgersByIndex.end()) && (it->second != ledgerHash)) @@ -487,7 +496,7 @@ LedgerHistory::fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash) void LedgerHistory::clearLedgerCachePrior(LedgerIndex seq) { - for (LedgerHash it : m_ledgers_by_hash.getKeys()) + for (LedgerHash const it : m_ledgers_by_hash.getKeys()) { auto const ledger = getLedgerByHash(it); if (!ledger || ledger->header().seq < seq) diff --git a/src/xrpld/app/ledger/LedgerHistory.h b/src/xrpld/app/ledger/LedgerHistory.h index 6ec279b28a..b988d152ad 100644 --- a/src/xrpld/app/ledger/LedgerHistory.h +++ b/src/xrpld/app/ledger/LedgerHistory.h @@ -1,9 +1,9 @@ #pragma once -#include #include #include +#include #include #include diff --git a/src/xrpld/app/ledger/LedgerHolder.h b/src/xrpld/app/ledger/LedgerHolder.h index 8d2ac9f308..bf9d478c40 100644 --- a/src/xrpld/app/ledger/LedgerHolder.h +++ b/src/xrpld/app/ledger/LedgerHolder.h @@ -28,7 +28,7 @@ public: LogicError("LedgerHolder::set with nullptr"); if (!ledger->isImmutable()) LogicError("LedgerHolder::set with mutable Ledger"); - std::lock_guard sl(m_lock); + std::lock_guard const sl(m_lock); m_heldLedger = std::move(ledger); } @@ -36,14 +36,14 @@ public: std::shared_ptr get() { - std::lock_guard sl(m_lock); + std::lock_guard const sl(m_lock); return m_heldLedger; } bool empty() { - std::lock_guard sl(m_lock); + std::lock_guard const sl(m_lock); return m_heldLedger == nullptr; } diff --git a/src/xrpld/app/ledger/LedgerMaster.h b/src/xrpld/app/ledger/LedgerMaster.h index d26376f4f2..35b3b71900 100644 --- a/src/xrpld/app/ledger/LedgerMaster.h +++ b/src/xrpld/app/ledger/LedgerMaster.h @@ -2,17 +2,18 @@ #include #include -#include #include #include #include #include -#include +#include #include #include #include #include +#include +#include #include #include #include @@ -389,7 +390,7 @@ private: void collect_metrics() { - std::lock_guard lock(m_mutex); + std::lock_guard const lock(m_mutex); m_stats.validatedLedgerAge.set(getValidatedLedgerAge().count()); m_stats.publishedLedgerAge.set(getPublishedLedgerAge().count()); } diff --git a/src/xrpld/app/ledger/LedgerPersistence.h b/src/xrpld/app/ledger/LedgerPersistence.h new file mode 100644 index 0000000000..e131932af4 --- /dev/null +++ b/src/xrpld/app/ledger/LedgerPersistence.h @@ -0,0 +1,90 @@ +#pragma once + +#include + +#include +#include + +namespace xrpl { + +class ServiceRegistry; +struct Fees; + +/** Save, or arrange to save, a fully-validated ledger. + + @param registry The service registry providing access to required services. + @param ledger The fully-validated ledger to save. + @param isSynchronous If true, wait for the save to complete. + @param isCurrent If true, the ledger is the current validated ledger. + + @return false on error. +*/ +bool +pendSaveValidated( + ServiceRegistry& registry, + std::shared_ptr const& ledger, + bool isSynchronous, + bool isCurrent); + +/** Make ledger using info loaded from database. + + @param info Ledger information. + @param rules Rules to use (may be overwritten by setup()). + @param fees Fees to use (may be overwritten by setup()). + @param registry Service registry for dependency injection. + @param acquire Acquire the ledger if not found locally. + @return Shared pointer to the ledger. +*/ +std::shared_ptr +loadLedgerHelper( + LedgerHeader const& info, + Rules const& rules, + Fees const& fees, + ServiceRegistry& registry, + bool acquire); + +/** Load a ledger by its sequence number. + + @param ledgerIndex The sequence number of the ledger to load. + @param rules Rules to use (may be overwritten by setup()). + @param fees Fees to use (may be overwritten by setup()). + @param registry Service registry for dependency injection. + @param acquire Acquire the ledger if not found locally. + @return Shared pointer to the ledger, or nullptr if not found. +*/ +std::shared_ptr +loadByIndex( + std::uint32_t ledgerIndex, + Rules const& rules, + Fees const& fees, + ServiceRegistry& registry, + bool acquire = true); + +/** Load a ledger by its hash. + + @param ledgerHash The hash of the ledger to load. + @param rules Rules to use (may be overwritten by setup()). + @param fees Fees to use (may be overwritten by setup()). + @param registry Service registry for dependency injection. + @param acquire Acquire the ledger if not found locally. + @return Shared pointer to the ledger, or nullptr if not found. +*/ +std::shared_ptr +loadByHash( + uint256 const& ledgerHash, + Rules const& rules, + Fees const& fees, + ServiceRegistry& registry, + bool acquire = true); + +/** Fetch the ledger with the highest sequence contained in the database. + + @param rules Rules to use (may be overwritten by setup()). + @param fees Fees to use (may be overwritten by setup()). + @param registry Service registry for dependency injection. + @return Tuple of (ledger, sequence, hash), or empty if not found. +*/ +std::tuple, std::uint32_t, uint256> +getLatestLedger(Rules const& rules, Fees const& fees, ServiceRegistry& registry); + +} // namespace xrpl diff --git a/src/xrpld/app/ledger/LedgerReplay.h b/src/xrpld/app/ledger/LedgerReplay.h index f4cbf9c69e..a2b2e60e8e 100644 --- a/src/xrpld/app/ledger/LedgerReplay.h +++ b/src/xrpld/app/ledger/LedgerReplay.h @@ -4,6 +4,7 @@ #include #include +#include namespace xrpl { diff --git a/src/xrpld/app/ledger/LedgerReplayTask.h b/src/xrpld/app/ledger/LedgerReplayTask.h index 028e7ba0bd..030121b240 100644 --- a/src/xrpld/app/ledger/LedgerReplayTask.h +++ b/src/xrpld/app/ledger/LedgerReplayTask.h @@ -78,7 +78,7 @@ public: InboundLedgers& inboundLedgers, LedgerReplayer& replayer, std::shared_ptr& skipListAcquirer, - TaskParameter&& parameter); + TaskParameter const& parameter); ~LedgerReplayTask(); diff --git a/src/xrpld/app/ledger/LedgerReplayer.h b/src/xrpld/app/ledger/LedgerReplayer.h index 218d22fb07..ce2acb0699 100644 --- a/src/xrpld/app/ledger/LedgerReplayer.h +++ b/src/xrpld/app/ledger/LedgerReplayer.h @@ -103,21 +103,21 @@ public: std::size_t tasksSize() const { - std::lock_guard lock(mtx_); + std::lock_guard const lock(mtx_); return tasks_.size(); } std::size_t deltasSize() const { - std::lock_guard lock(mtx_); + std::lock_guard const lock(mtx_); return deltas_.size(); } std::size_t skipListsSize() const { - std::lock_guard lock(mtx_); + std::lock_guard const lock(mtx_); return skipLists_.size(); } diff --git a/src/xrpld/app/ledger/LedgerToJson.h b/src/xrpld/app/ledger/LedgerToJson.h index 9def1c3826..3686311656 100644 --- a/src/xrpld/app/ledger/LedgerToJson.h +++ b/src/xrpld/app/ledger/LedgerToJson.h @@ -1,11 +1,11 @@ #pragma once -#include #include #include #include #include +#include #include namespace xrpl { diff --git a/src/xrpld/app/ledger/LocalTxs.h b/src/xrpld/app/ledger/LocalTxs.h index 34dfede40f..cb17be5bdb 100644 --- a/src/xrpld/app/ledger/LocalTxs.h +++ b/src/xrpld/app/ledger/LocalTxs.h @@ -1,7 +1,6 @@ #pragma once -#include - +#include #include #include diff --git a/src/xrpld/app/ledger/OpenLedger.h b/src/xrpld/app/ledger/OpenLedger.h index 76123bafae..5b1d611b44 100644 --- a/src/xrpld/app/ledger/OpenLedger.h +++ b/src/xrpld/app/ledger/OpenLedger.h @@ -1,7 +1,5 @@ #pragma once -#include -#include #include #include @@ -9,6 +7,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/xrpld/app/ledger/OrderBookDBImpl.cpp b/src/xrpld/app/ledger/OrderBookDBImpl.cpp index b82fa3896b..add9c7eea9 100644 --- a/src/xrpld/app/ledger/OrderBookDBImpl.cpp +++ b/src/xrpld/app/ledger/OrderBookDBImpl.cpp @@ -1,11 +1,10 @@ #include #include -#include #include +#include #include #include -#include namespace xrpl { @@ -14,7 +13,7 @@ OrderBookDBImpl::OrderBookDBImpl(ServiceRegistry& registry, OrderBookDBConfig co , pathSearchMax_(config.pathSearchMax) , standalone_(config.standalone) , seq_(0) - , j_(registry.journal("OrderBookDB")) + , j_(registry.getJournal("OrderBookDB")) { } @@ -27,7 +26,7 @@ make_OrderBookDB(ServiceRegistry& registry, OrderBookDBConfig const& config) void OrderBookDBImpl::setup(std::shared_ptr const& ledger) { - if (!standalone_ && registry_.getOPs().isNeedNetworkLedger()) + if (!standalone_ && registry_.get().getOPs().isNeedNetworkLedger()) { JLOG(j_.warn()) << "Eliding full order book update: no ledger"; return; @@ -52,12 +51,18 @@ OrderBookDBImpl::setup(std::shared_ptr const& ledger) if (pathSearchMax_ != 0) { if (standalone_) + { update(ledger); + } else - registry_.getJobQueue().addJob( - jtUPDATE_PF, "OrderBookUpd" + std::to_string(ledger->seq()), [this, ledger]() { + { + // Shorten job name to fit Linux 15-char thread name limit with "j:" prefix + // "OB" + seq (max 9 digits) = 11 chars, + "j:" = 13 chars (fits in 15) + registry_.get().getJobQueue().addJob( + jtUPDATE_PF, "OB" + std::to_string(ledger->seq() % 1000000000), [this, ledger]() { update(ledger); }); + } } } @@ -92,7 +97,7 @@ OrderBookDBImpl::update(std::shared_ptr const& ledger) { for (auto& sle : ledger->sles) { - if (registry_.isStopping()) + if (registry_.get().isStopping()) { JLOG(j_.info()) << "Update halted because the process is stopping"; seq_.store(0); @@ -111,14 +116,22 @@ OrderBookDBImpl::update(std::shared_ptr const& ledger) book.domain = (*sle)[~sfDomainID]; if (book.domain) + { domainBooks[{book.in, *book.domain}].insert(book.out); + } else + { allBooks[book.in].insert(book.out); + } if (book.domain && isXRP(book.out)) + { xrpDomainBooks.insert({book.in, *book.domain}); + } else if (isXRP(book.out)) + { xrpBooks.insert(book.in); + } ++cnt; } @@ -149,32 +162,40 @@ OrderBookDBImpl::update(std::shared_ptr const& ledger) JLOG(j_.debug()) << "Update completed (" << ledger->seq() << "): " << cnt << " books found"; { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); allBooks_.swap(allBooks); xrpBooks_.swap(xrpBooks); domainBooks_.swap(domainBooks); xrpDomainBooks_.swap(xrpDomainBooks); } - registry_.getLedgerMaster().newOrderBookDB(); + registry_.get().getLedgerMaster().newOrderBookDB(); } void OrderBookDBImpl::addOrderBook(Book const& book) { - bool toXRP = isXRP(book.out); + bool const toXRP = isXRP(book.out); - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); if (book.domain) + { domainBooks_[{book.in, *book.domain}].insert(book.out); + } else + { allBooks_[book.in].insert(book.out); + } if (book.domain && toXRP) + { xrpDomainBooks_.insert({book.in, *book.domain}); + } else if (toXRP) + { xrpBooks_.insert(book.in); + } } // return list of all orderbooks that want this issuerID and currencyID @@ -184,7 +205,7 @@ OrderBookDBImpl::getBooksByTakerPays(Issue const& issue, std::optional std::vector ret; { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); auto getBooks = [&](auto const& container, auto const& key) { if (auto it = container.find(key); it != container.end()) @@ -198,9 +219,13 @@ OrderBookDBImpl::getBooksByTakerPays(Issue const& issue, std::optional }; if (!domain) + { getBooks(allBooks_, issue); + } else + { getBooks(domainBooks_, std::make_pair(issue, *domain)); + } } return ret; @@ -209,7 +234,7 @@ OrderBookDBImpl::getBooksByTakerPays(Issue const& issue, std::optional int OrderBookDBImpl::getBookSize(Issue const& issue, std::optional const& domain) { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); if (!domain) { @@ -226,9 +251,9 @@ OrderBookDBImpl::getBookSize(Issue const& issue, std::optional const& d } bool -OrderBookDBImpl::isBookToXRP(Issue const& issue, std::optional domain) +OrderBookDBImpl::isBookToXRP(Issue const& issue, std::optional const& domain) { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); if (domain) return xrpDomainBooks_.contains({issue, *domain}); return xrpBooks_.contains(issue); @@ -237,7 +262,7 @@ OrderBookDBImpl::isBookToXRP(Issue const& issue, std::optional domain) BookListeners::pointer OrderBookDBImpl::makeBookListeners(Book const& book) { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); auto ret = getBookListeners(book); if (!ret) @@ -258,7 +283,7 @@ BookListeners::pointer OrderBookDBImpl::getBookListeners(Book const& book) { BookListeners::pointer ret; - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); auto it0 = mListeners.find(book); if (it0 != mListeners.end()) @@ -275,7 +300,7 @@ OrderBookDBImpl::processTxn( AcceptedLedgerTx const& alTx, MultiApiJson const& jvObj) { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); // For this particular transaction, maintain the set of unique // subscriptions that have already published it. This prevents sending @@ -306,11 +331,17 @@ OrderBookDBImpl::processTxn( // We need a field that contains the TakerGets and TakerPays // parameters. if (node.getFName() == sfModifiedNode) + { process(sfPreviousFields); + } else if (node.getFName() == sfCreatedNode) + { process(sfNewFields); + } else if (node.getFName() == sfDeletedNode) + { process(sfFinalFields); + } } } catch (std::exception const& ex) diff --git a/src/xrpld/app/ledger/OrderBookDBImpl.h b/src/xrpld/app/ledger/OrderBookDBImpl.h index d9043f942e..4dd413438e 100644 --- a/src/xrpld/app/ledger/OrderBookDBImpl.h +++ b/src/xrpld/app/ledger/OrderBookDBImpl.h @@ -48,7 +48,7 @@ public: getBookSize(Issue const& issue, std::optional const& domain = std::nullopt) override; bool - isBookToXRP(Issue const& issue, std::optional domain = std::nullopt) override; + isBookToXRP(Issue const& issue, std::optional const& domain = std::nullopt) override; // OrderBookDBImpl-specific methods void @@ -67,7 +67,7 @@ public: makeBookListeners(Book const&) override; private: - ServiceRegistry& registry_; + std::reference_wrapper registry_; int const pathSearchMax_; bool const standalone_; diff --git a/src/xrpld/app/ledger/README.md b/src/xrpld/app/ledger/README.md index cb935897b8..7dc38ce9fa 100644 --- a/src/xrpld/app/ledger/README.md +++ b/src/xrpld/app/ledger/README.md @@ -63,7 +63,7 @@ that the validator sends its proposals and validations to the network. ## Ledger Priorities -There are two ledgers that are the most important for a rippled server to have: +There are two ledgers that are the most important for an xrpld server to have: - The consensus ledger and - The last validated ledger. @@ -224,7 +224,7 @@ conclusion about which last closed ledger is authoritative. ## Consensus -A distributed agreement protocol. Ripple uses the consensus process to solve +A distributed agreement protocol. XRPL uses the consensus process to solve the problem of double-spending. ## Validation @@ -402,7 +402,7 @@ are occupied by the exchange rate. ## Overview -The Ripple server permits clients to subscribe to a continuous stream of +The XRPL server permits clients to subscribe to a continuous stream of fully-validated ledgers. The publication code maintains this stream. The server attempts to maintain this continuous stream unless it falls diff --git a/src/xrpld/app/ledger/detail/BuildLedger.cpp b/src/xrpld/app/ledger/detail/BuildLedger.cpp index c4f8f9e60a..3b48ab13c5 100644 --- a/src/xrpld/app/ledger/detail/BuildLedger.cpp +++ b/src/xrpld/app/ledger/detail/BuildLedger.cpp @@ -1,10 +1,10 @@ #include -#include #include #include #include -#include +#include +#include #include #include @@ -138,11 +138,11 @@ applyTransactions( count += changes; // A non-retry pass made no changes - if (!changes && !certainRetry) + if ((changes == 0) && !certainRetry) break; // Stop retriable passes - if (!changes || (pass >= LEDGER_RETRY_PASSES)) + if ((changes == 0) || (pass >= LEDGER_RETRY_PASSES)) certainRetry = false; } @@ -181,10 +181,12 @@ buildLedger( auto const applied = applyTransactions(app, built, txns, failedTxns, accum, j); if (!txns.empty() || !failedTxns.empty()) + { JLOG(j.debug()) << "Applied " << applied << " transactions; " << failedTxns.size() << " failed and " << txns.size() << " will be retried. " << "Total transactions in ledger (including Inner Batch): " << accum.txCount(); + } else JLOG(j.debug()) << "Applied " << applied << " transactions. " << "Total transactions in ledger (including Inner Batch): " diff --git a/src/xrpld/app/ledger/detail/InboundLedger.cpp b/src/xrpld/app/ledger/detail/InboundLedger.cpp index b8158e507f..2402b5b561 100644 --- a/src/xrpld/app/ledger/detail/InboundLedger.cpp +++ b/src/xrpld/app/ledger/detail/InboundLedger.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -66,16 +65,10 @@ InboundLedger::InboundLedger( hash, ledgerAcquireTimeout, {jtLEDGER_DATA, "InboundLedger", 5}, - app.journal("InboundLedger")) + app.getJournal("InboundLedger")) , m_clock(clock) - , mHaveHeader(false) - , mHaveState(false) - , mHaveTransactions(false) - , mSignaled(false) - , mByHash(true) , mSeq(seq) , mReason(reason) - , mReceiveDispatched(false) , mPeerSet(std::move(peerSet)) { JLOG(journal_.trace()) << "Acquiring ledger " << hash_; @@ -121,14 +114,14 @@ InboundLedger::getPeerCount() const { auto const& peerIds = mPeerSet->getPeerIds(); return std::count_if(peerIds.begin(), peerIds.end(), [this](auto id) { - return (app_.overlay().findPeerByShortID(id) != nullptr); + return (app_.getOverlay().findPeerByShortID(id) != nullptr); }); } void InboundLedger::update(std::uint32_t seq) { - ScopedLockType sl(mtx_); + ScopedLockType const sl(mtx_); // If we didn't know the sequence number, but now do, save it if ((seq != 0) && (mSeq == 0)) @@ -141,13 +134,17 @@ InboundLedger::update(std::uint32_t seq) bool InboundLedger::checkLocal() { - ScopedLockType sl(mtx_); + ScopedLockType const sl(mtx_); if (!isDone()) { if (mLedger) + { tryDB(mLedger->stateMap().family().db()); + } else + { tryDB(app_.getNodeFamily().db()); + } if (failed_ || complete_) { done(); @@ -184,7 +181,9 @@ neededHashes(uint256 const& root, SHAMap& map, int max, SHAMapSyncFilter* filter if (!root.isZero()) { if (map.getHash().isZero()) + { ret.push_back(root); + } else { auto mn = map.getMissingNodes(max, filter); @@ -218,8 +217,9 @@ InboundLedger::tryDB(NodeStore::Database& srcDB) { auto makeLedger = [&, this](Blob const& data) { JLOG(journal_.trace()) << "Ledger header found in fetch pack"; + Rules const rules{app_.config().features}; mLedger = std::make_shared( - deserializePrefixedHeader(makeSlice(data)), app_.config(), app_.getNodeFamily()); + deserializePrefixedHeader(makeSlice(data)), rules, app_.getNodeFamily()); if (mLedger->header().hash != hash_ || (mSeq != 0 && mSeq != mLedger->header().seq)) { // We know for a fact the ledger can never be acquired @@ -357,7 +357,7 @@ InboundLedger::onTimer(bool wasProgress, ScopedLockType&) mByHash = true; - std::size_t pc = getPeerCount(); + std::size_t const pc = getPeerCount(); JLOG(journal_.debug()) << "No progress(" << pc << ") for ledger " << hash_; // addPeers triggers if the reason is not HISTORY @@ -435,7 +435,9 @@ InboundLedger::done() self->app_.getLedgerMaster().tryAdvance(); } else + { self->app_.getInboundLedgers().logFailure(self->hash_, self->mSeq); + } }); } @@ -461,9 +463,13 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) ss << " from " << peer; if (complete_ || failed_) + { ss << " complete=" << complete_ << " failed=" << failed_; + } else + { ss << " header=" << mHaveHeader << " tx=" << mHaveTransactions << " as=" << mHaveState; + } stream << ss.str(); } @@ -517,7 +523,7 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) auto packet = std::make_shared(tmBH, protocol::mtGET_OBJECTS); auto const& peerIds = mPeerSet->getPeerIds(); std::for_each(peerIds.begin(), peerIds.end(), [this, &packet](auto id) { - if (auto p = app_.overlay().findPeerByShortID(id)) + if (auto p = app_.getOverlay().findPeerByShortID(id)) { mByHash = false; p->send(packet); @@ -562,7 +568,9 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) tmGL.set_querydepth(2); } else + { tmGL.set_querydepth(1); + } // Get the state data first because it's the most likely to be useful // if we wind up abandoning this fetch. @@ -602,7 +610,9 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) if (nodes.empty()) { if (!mLedger->stateMap().isValid()) + { failed_ = true; + } else { mHaveState = true; @@ -628,10 +638,8 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) mPeerSet->sendRequest(tmGL, peer); return; } - else - { - JLOG(journal_.trace()) << "All AS nodes filtered"; - } + + JLOG(journal_.trace()) << "All AS nodes filtered"; } } } @@ -667,7 +675,9 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) if (nodes.empty()) { if (!mLedger->txMap().isValid()) + { failed_ = true; + } else { mHaveTransactions = true; @@ -692,10 +702,8 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) mPeerSet->sendRequest(tmGL, peer); return; } - else - { - JLOG(journal_.trace()) << "All TX nodes filtered"; - } + + JLOG(journal_.trace()) << "All TX nodes filtered"; } } } @@ -763,7 +771,8 @@ InboundLedger::takeHeader(std::string const& data) return true; auto* f = &app_.getNodeFamily(); - mLedger = std::make_shared(deserializeHeader(makeSlice(data)), app_.config(), *f); + Rules const rules{app_.config().features}; + mLedger = std::make_shared(deserializeHeader(makeSlice(data)), rules, *f); if (mLedger->header().hash != hash_ || (mSeq != 0 && mSeq != mLedger->header().seq)) { JLOG(journal_.warn()) << "Acquire hash mismatch: " << mLedger->header().hash @@ -823,11 +832,13 @@ InboundLedger::receiveNode(protocol::TMLedgerData& packet, SHAMapAddNode& san) auto [map, rootHash, filter] = [&]() -> std::tuple> { if (packet.type() == protocol::liTX_NODE) + { return { mLedger->txMap(), SHAMapHash{mLedger->header().txHash}, std::make_unique( mLedger->txMap().family().db(), app_.getLedgerMaster())}; + } return { mLedger->stateMap(), SHAMapHash{mLedger->header().accountHash}, @@ -872,9 +883,13 @@ InboundLedger::receiveNode(protocol::TMLedgerData& packet, SHAMapAddNode& san) if (!map.isSynching()) { if (packet.type() == protocol::liTX_NODE) + { mHaveTransactions = true; + } else + { mHaveState = true; + } if (mHaveTransactions && mHaveState) { @@ -975,7 +990,7 @@ InboundLedger::gotData( std::weak_ptr peer, std::shared_ptr const& data) { - std::lock_guard sl(mReceivedDataLock); + std::lock_guard const sl(mReceivedDataLock); if (isDone()) return false; @@ -1011,7 +1026,7 @@ InboundLedger::processData(std::shared_ptr peer, protocol::TMLedgerData& p SHAMapAddNode san; - ScopedLockType sl(mtx_); + ScopedLockType const sl(mtx_); try { @@ -1063,7 +1078,7 @@ InboundLedger::processData(std::shared_ptr peer, protocol::TMLedgerData& p return -1; } - ScopedLockType sl(mtx_); + ScopedLockType const sl(mtx_); // Verify node IDs and data are complete for (auto const& node : packet.nodes()) @@ -1129,9 +1144,13 @@ struct PeerDataCounts while (i != counts.end()) { if (i->second < thresh) + { i = counts.erase(i); + } else + { ++i; + } } } @@ -1183,7 +1202,7 @@ InboundLedger::runData() data.clear(); { - std::lock_guard sl(mReceivedDataLock); + std::lock_guard const sl(mReceivedDataLock); if (mReceivedData.empty()) { @@ -1198,7 +1217,7 @@ InboundLedger::runData() { if (auto peer = entry.first.lock()) { - int count = processData(peer, *(entry.second)); + int const count = processData(peer, *(entry.second)); dataCounts.update(std::move(peer), count); } } @@ -1217,7 +1236,7 @@ InboundLedger::getJson(int) { Json::Value ret(Json::objectValue); - ScopedLockType sl(mtx_); + ScopedLockType const sl(mtx_); ret[jss::hash] = to_string(hash_); diff --git a/src/xrpld/app/ledger/detail/InboundLedgers.cpp b/src/xrpld/app/ledger/detail/InboundLedgers.cpp index e17437d64f..f147a35ca4 100644 --- a/src/xrpld/app/ledger/detail/InboundLedgers.cpp +++ b/src/xrpld/app/ledger/detail/InboundLedgers.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -38,7 +37,7 @@ public: std::unique_ptr peerSetBuilder) : app_(app) , fetchRate_(clock.now()) - , j_(app.journal("InboundLedger")) + , j_(app.getJournal("InboundLedger")) , m_clock(clock) , mRecentFailures(clock) , mCounter(collector->make_counter("ledger_fetches")) @@ -111,7 +110,7 @@ public: if (pendingAcquires_.contains(hash)) return; pendingAcquires_.insert(hash); - scope_unlock unlock(lock); + scope_unlock const unlock(lock); acquire(hash, seq, reason); } catch (std::exception const& e) @@ -134,7 +133,7 @@ public: std::shared_ptr ret; { - ScopedLockType sl(mLock); + ScopedLockType const sl(mLock); auto it = mLedgers.find(hash); if (it != mLedgers.end()) @@ -174,8 +173,10 @@ public: // Stash the data for later processing and see if we need to // dispatch if (ledger->gotData(std::weak_ptr(peer), packet)) + { app_.getJobQueue().addJob( jtLEDGER_DATA, "ProcessLData", [ledger]() { ledger->runData(); }); + } return true; } @@ -196,7 +197,7 @@ public: void logFailure(uint256 const& h, std::uint32_t seq) override { - ScopedLockType sl(mLock); + ScopedLockType const sl(mLock); mRecentFailures.emplace(h, seq); } @@ -204,7 +205,7 @@ public: bool isFailure(uint256 const& h) override { - ScopedLockType sl(mLock); + ScopedLockType const sl(mLock); beast::expire(mRecentFailures, kReacquireInterval); return mRecentFailures.find(h) != mRecentFailures.end(); @@ -249,7 +250,7 @@ public: void clearFailures() override { - ScopedLockType sl(mLock); + ScopedLockType const sl(mLock); mRecentFailures.clear(); mLedgers.clear(); @@ -258,7 +259,7 @@ public: std::size_t fetchRate() override { - std::lock_guard lock(fetchRateMutex_); + std::lock_guard const lock(fetchRateMutex_); return 60 * fetchRate_.value(m_clock.now()); } @@ -267,7 +268,7 @@ public: void onLedgerFetched() override { - std::lock_guard lock(fetchRateMutex_); + std::lock_guard const lock(fetchRateMutex_); fetchRate_.add(1, m_clock.now()); } @@ -279,7 +280,7 @@ public: std::vector>> acqs; { - ScopedLockType sl(mLock); + ScopedLockType const sl(mLock); acqs.reserve(mLedgers.size()); for (auto const& it : mLedgers) @@ -290,20 +291,28 @@ public: for (auto const& it : mRecentFailures) { if (it.second > 1) + { ret[std::to_string(it.second)][jss::failed] = true; + } else + { ret[to_string(it.first)][jss::failed] = true; + } } } for (auto const& it : acqs) { // getJson is expensive, so call without the lock - std::uint32_t seq = it.second->getSeq(); + std::uint32_t const seq = it.second->getSeq(); if (seq > 1) + { ret[std::to_string(seq)] = it.second->getJson(0); + } else + { ret[to_string(it.first)] = it.second->getJson(0); + } } return ret; @@ -314,7 +323,7 @@ public: { std::vector> acquires; { - ScopedLockType sl(mLock); + ScopedLockType const sl(mLock); acquires.reserve(mLedgers.size()); for (auto const& it : mLedgers) @@ -340,10 +349,10 @@ public: // Make a list of things to sweep, while holding the lock std::vector stuffToSweep; - std::size_t total; + std::size_t total = 0; { - ScopedLockType sl(mLock); + ScopedLockType const sl(mLock); MapType::iterator it(mLedgers.begin()); total = mLedgers.size(); @@ -384,7 +393,7 @@ public: void stop() override { - ScopedLockType lock(mLock); + ScopedLockType const lock(mLock); stopping_ = true; mLedgers.clear(); mRecentFailures.clear(); @@ -393,7 +402,7 @@ public: std::size_t cacheSize() override { - ScopedLockType lock(mLock); + ScopedLockType const lock(mLock); return mLedgers.size(); } diff --git a/src/xrpld/app/ledger/detail/InboundTransactions.cpp b/src/xrpld/app/ledger/detail/InboundTransactions.cpp index b6f4c961c1..cc3585a3fb 100644 --- a/src/xrpld/app/ledger/detail/InboundTransactions.cpp +++ b/src/xrpld/app/ledger/detail/InboundTransactions.cpp @@ -3,12 +3,12 @@ #include #include -#include #include #include #include #include +#include #include #include @@ -50,11 +50,10 @@ public: std::function const&, bool)> gotSet, std::unique_ptr peerSetBuilder) : app_(app) - , m_seq(0) , m_zeroSet(m_map[uint256()]) , m_gotSet(std::move(gotSet)) , m_peerSetBuilder(std::move(peerSetBuilder)) - , j_(app_.journal("InboundTransactions")) + , j_(app_.getJournal("InboundTransactions")) { m_zeroSet.mSet = std::make_shared(SHAMapType::TRANSACTION, uint256(), app_.getNodeFamily()); @@ -65,7 +64,7 @@ public: getAcquire(uint256 const& hash) { { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); auto it = m_map.find(hash); @@ -81,7 +80,7 @@ public: TransactionAcquire::pointer ta; { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); if (auto it = m_map.find(hash); it != m_map.end()) { @@ -119,12 +118,12 @@ public: std::shared_ptr peer, std::shared_ptr packet_ptr) override { - protocol::TMLedgerData& packet = *packet_ptr; + protocol::TMLedgerData const& packet = *packet_ptr; JLOG(j_.trace()) << "Got data (" << packet.nodes().size() << ") for acquiring ledger: " << hash; - TransactionAcquire::pointer ta = getAcquire(hash); + TransactionAcquire::pointer const ta = getAcquire(hash); if (ta == nullptr) { @@ -164,17 +163,20 @@ public: bool isNew = true; { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); auto& inboundSet = m_map[hash]; - if (inboundSet.mSeq < m_seq) - inboundSet.mSeq = m_seq; + inboundSet.mSeq = std::max(inboundSet.mSeq, m_seq); if (inboundSet.mSet) + { isNew = false; + } else + { inboundSet.mSet = set; + } inboundSet.mAcquire.reset(); } @@ -186,7 +188,7 @@ public: void newRound(std::uint32_t seq) override { - std::lock_guard lock(mLock); + std::lock_guard const lock(mLock); // Protect zero set from expiration m_zeroSet.mSeq = seq; @@ -198,14 +200,18 @@ public: auto it = m_map.begin(); std::uint32_t const minSeq = (seq < setKeepRounds) ? 0 : (seq - setKeepRounds); - std::uint32_t maxSeq = seq + setKeepRounds; + std::uint32_t const maxSeq = seq + setKeepRounds; while (it != m_map.end()) { if (it->second.mSeq < minSeq || it->second.mSeq > maxSeq) + { it = m_map.erase(it); + } else + { ++it; + } } } } @@ -213,7 +219,7 @@ public: void stop() override { - std::lock_guard lock(mLock); + std::lock_guard const lock(mLock); stopping_ = true; m_map.clear(); } @@ -227,7 +233,7 @@ private: bool stopping_{false}; MapType m_map; - std::uint32_t m_seq; + std::uint32_t m_seq{0}; // The empty transaction set whose hash is zero InboundTransactionSet& m_zeroSet; diff --git a/src/xrpld/app/ledger/detail/LedgerCleaner.cpp b/src/xrpld/app/ledger/detail/LedgerCleaner.cpp index ad9c6a277d..a0d168f299 100644 --- a/src/xrpld/app/ledger/detail/LedgerCleaner.cpp +++ b/src/xrpld/app/ledger/detail/LedgerCleaner.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include @@ -74,7 +76,7 @@ public: { JLOG(j_.info()) << "Stopping"; { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); shouldExit_ = true; wakeup_.notify_one(); } @@ -90,10 +92,12 @@ public: void onWrite(beast::PropertyStream::Map& map) override { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); if (maxRange_ == 0) + { map["status"] = "idle"; + } else { map["status"] = "running"; @@ -120,7 +124,7 @@ public: app_.getLedgerMaster().getFullValidatedRange(minRange, maxRange); { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); maxRange_ = maxRange; minRange_ = minRange; @@ -259,7 +263,9 @@ private: return false; } - auto dbLedger = loadByIndex(ledgerIndex, app_); + Rules const rules{app_.config().features}; + 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)) { @@ -274,7 +280,7 @@ private: doTxns = true; } - if (doNodes && !nodeLedger->walkLedger(app_.journal("Ledger"))) + if (doNodes && !nodeLedger->walkLedger(app_.getJournal("Ledger"))) { JLOG(j_.debug()) << "Ledger " << ledgerIndex << " is missing nodes"; app_.getLedgerMaster().clearLedger(ledgerIndex); @@ -321,8 +327,8 @@ private: // No. Try to get another ledger that might have the hash we // need: compute the index and hash of a ledger that will have // the hash we need. - LedgerIndex refIndex = getCandidateLedger(ledgerIndex); - LedgerHash refHash = getLedgerHash(referenceLedger, refIndex); + LedgerIndex const refIndex = getCandidateLedger(ledgerIndex); + LedgerHash const refHash = getLedgerHash(referenceLedger, refIndex); bool const nonzero(refHash.isNonZero()); XRPL_ASSERT(nonzero, "xrpl::LedgerCleanerImp::getHash : nonzero hash"); @@ -348,7 +354,7 @@ private: doLedgerCleaner() { auto shouldExit = [this] { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return shouldExit_; }; @@ -356,10 +362,10 @@ private: while (!shouldExit()) { - LedgerIndex ledgerIndex; + LedgerIndex ledgerIndex = 0; LedgerHash ledgerHash; - bool doNodes; - bool doTxns; + bool doNodes = false; + bool doTxns = false; if (app_.getFeeTrack().isLoadedLocal()) { @@ -369,7 +375,7 @@ private: } { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); if ((minRange_ > maxRange_) || (maxRange_ == 0) || (minRange_ == 0)) { minRange_ = maxRange_ = 0; @@ -397,7 +403,7 @@ private: if (fail) { { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); ++failures_; } // Wait for acquiring to catch up to us @@ -406,7 +412,7 @@ private: else { { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); if (ledgerIndex == minRange_) ++minRange_; if (ledgerIndex == maxRange_) diff --git a/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.cpp b/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.cpp index e22351fc77..f829e58830 100644 --- a/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.cpp +++ b/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.cpp @@ -21,7 +21,7 @@ LedgerDeltaAcquire::LedgerDeltaAcquire( ledgerHash, LedgerReplayParameters::SUB_TASK_TIMEOUT, {jtREPLAY_TASK, "LedReplDelta", LedgerReplayParameters::MAX_QUEUED_TASKS}, - app.journal("LedgerReplayDelta")) + app.getJournal("LedgerReplayDelta")) , inboundLedgers_(inboundLedgers) , ledgerSeq_(ledgerSeq) , peerSet_(std::move(peerSet)) @@ -124,7 +124,8 @@ LedgerDeltaAcquire::processData( if (info.seq == ledgerSeq_) { // create a temporary ledger for building a LedgerReplay object later - replayTemp_ = std::make_shared(info, app_.config(), app_.getNodeFamily()); + Rules const rules{app_.config().features}; + replayTemp_ = std::make_shared(info, rules, app_.getNodeFamily()); if (replayTemp_) { complete_ = true; @@ -145,7 +146,7 @@ LedgerDeltaAcquire::addDataCallback(InboundLedger::Reason reason, OnDeltaDataCB& { ScopedLockType sl(mtx_); dataReadyCallbacks_.emplace_back(std::move(cb)); - if (reasons_.count(reason) == 0) + if (!reasons_.contains(reason)) { reasons_.emplace(reason); if (fullLedger_) @@ -177,7 +178,7 @@ LedgerDeltaAcquire::tryBuild(std::shared_ptr const& parent) parent->header().hash == replayTemp_->header().parentHash, "xrpl::LedgerDeltaAcquire::tryBuild : parent hash match"); // build ledger - LedgerReplay replayData(parent, replayTemp_, std::move(orderedTxns_)); + LedgerReplay const replayData(parent, replayTemp_, std::move(orderedTxns_)); fullLedger_ = buildLedger(replayData, tapNONE, app_, journal_); if (fullLedger_ && fullLedger_->header().hash == hash_) { @@ -185,14 +186,12 @@ LedgerDeltaAcquire::tryBuild(std::shared_ptr const& parent) onLedgerBuilt(sl); return fullLedger_; } - else - { - failed_ = true; - complete_ = false; - JLOG(journal_.error()) << "tryBuild failed " << hash_ << " with parent " - << parent->header().hash; - Throw("Cannot replay ledger"); - } + + failed_ = true; + complete_ = false; + JLOG(journal_.error()) << "tryBuild failed " << hash_ << " with parent " + << parent->header().hash; + Throw("Cannot replay ledger"); } void diff --git a/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.h b/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.h index 2877d997e6..9ac58c2e7c 100644 --- a/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.h +++ b/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.h @@ -1,11 +1,11 @@ #pragma once #include -#include #include #include #include +#include #include diff --git a/src/xrpld/app/ledger/detail/LedgerMaster.cpp b/src/xrpld/app/ledger/detail/LedgerMaster.cpp index 8f0f049388..dc843bc5a5 100644 --- a/src/xrpld/app/ledger/detail/LedgerMaster.cpp +++ b/src/xrpld/app/ledger/detail/LedgerMaster.cpp @@ -1,20 +1,18 @@ #include -#include #include +#include #include #include -#include #include #include #include #include #include -#include #include #include #include +#include -#include #include #include #include @@ -22,7 +20,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -94,7 +94,7 @@ LedgerMaster::LedgerMaster( 65536, std::chrono::seconds{45}, stopwatch, - app_.journal("TaggedCache")) + app_.getJournal("TaggedCache")) , m_stats(std::bind(&LedgerMaster::collect_metrics, this), collector) { } @@ -102,7 +102,7 @@ LedgerMaster::LedgerMaster( LedgerIndex LedgerMaster::getCurrentLedgerIndex() { - return app_.openLedger().current()->header().seq; + return app_.getOpenLedger().current()->header().seq; } LedgerIndex @@ -122,7 +122,7 @@ LedgerMaster::isCompatible(ReadView const& view, beast::Journal::Stream s, char } { - std::lock_guard sl(m_mutex); + std::lock_guard const sl(m_mutex); if ((mLastValidLedger.second != 0) && !areCompatible(mLastValidLedger.first, mLastValidLedger.second, view, s, reason)) @@ -138,14 +138,14 @@ std::chrono::seconds LedgerMaster::getPublishedLedgerAge() { using namespace std::chrono_literals; - std::chrono::seconds pubClose{mPubLedgerClose.load()}; + std::chrono::seconds const pubClose{mPubLedgerClose.load()}; if (pubClose == 0s) { JLOG(m_journal.debug()) << "No published ledger"; return weeks{2}; } - std::chrono::seconds ret = app_.timeKeeper().closeTime().time_since_epoch(); + std::chrono::seconds ret = app_.getTimeKeeper().closeTime().time_since_epoch(); ret -= pubClose; ret = (ret > 0s) ? ret : 0s; static std::chrono::seconds lastRet = -1s; @@ -163,14 +163,14 @@ LedgerMaster::getValidatedLedgerAge() { using namespace std::chrono_literals; - std::chrono::seconds valClose{mValidLedgerSign.load()}; + std::chrono::seconds const valClose{mValidLedgerSign.load()}; if (valClose == 0s) { JLOG(m_journal.debug()) << "No validated ledger"; return weeks{2}; } - std::chrono::seconds ret = app_.timeKeeper().closeTime().time_since_epoch(); + std::chrono::seconds ret = app_.getTimeKeeper().closeTime().time_since_epoch(); ret -= valClose; ret = (ret > 0s) ? ret : 0s; static std::chrono::seconds lastRet = -1s; @@ -193,9 +193,9 @@ LedgerMaster::isCaughtUp(std::string& reason) reason = "No recently-published ledger"; return false; } - std::uint32_t validClose = mValidLedgerSign.load(); - std::uint32_t pubClose = mPubLedgerClose.load(); - if (!validClose || !pubClose) + std::uint32_t const validClose = mValidLedgerSign.load(); + std::uint32_t const pubClose = mPubLedgerClose.load(); + if ((validClose == 0u) || (pubClose == 0u)) { reason = "No published ledger"; return false; @@ -216,7 +216,7 @@ LedgerMaster::setValidLedger(std::shared_ptr const& l) if (!standalone_) { - auto validations = app_.validators().negativeUNLFilter( + auto validations = app_.getValidators().negativeUNLFilter( app_.getValidations().getTrustedForLedger(l->header().hash, l->header().seq)); times.reserve(validations.size()); for (auto const& val : validations) @@ -228,7 +228,7 @@ LedgerMaster::setValidLedger(std::shared_ptr const& l) NetClock::time_point signTime; - if (!times.empty() && times.size() >= app_.validators().quorum()) + if (!times.empty() && times.size() >= app_.getValidators().quorum()) { // Calculate the sample median std::sort(times.begin(), times.end()); @@ -283,7 +283,9 @@ LedgerMaster::setValidLedger(std::shared_ptr const& l) app_.getOPs().setAmendmentWarned(); } else + { app_.getOPs().clearAmendmentWarned(); + } } } } @@ -299,7 +301,7 @@ LedgerMaster::setPubLedger(std::shared_ptr const& l) void LedgerMaster::addHeldTransaction(std::shared_ptr const& transaction) { - std::lock_guard ml(m_mutex); + std::lock_guard const ml(m_mutex); mHeldTransactions.insert(transaction->getSTransaction()); } @@ -327,7 +329,7 @@ LedgerMaster::canBeCurrent(std::shared_ptr const& ledger) // we perform this check. Otherwise, we only do it if we've built a // few ledgers as our clock can be off when we first start up - auto closeTime = app_.timeKeeper().closeTime(); + auto closeTime = app_.getTimeKeeper().closeTime(); auto ledgerClose = ledger->header().parentCloseTime; using namespace std::chrono_literals; @@ -350,10 +352,12 @@ LedgerMaster::canBeCurrent(std::shared_ptr const& ledger) LedgerIndex maxSeq = validLedger->header().seq + 10; if (closeTime > validLedger->header().parentCloseTime) + { maxSeq += std::chrono::duration_cast( closeTime - validLedger->header().parentCloseTime) .count() / 2; + } if (ledger->header().seq > maxSeq) { @@ -380,7 +384,7 @@ LedgerMaster::switchLCL(std::shared_ptr const& lastClosed) LogicError("The new last closed ledger is open!"); { - std::lock_guard ml(m_mutex); + std::lock_guard const ml(m_mutex); mClosedLedger.set(lastClosed); } @@ -404,9 +408,9 @@ LedgerMaster::fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash) bool LedgerMaster::storeLedger(std::shared_ptr ledger) { - bool validated = ledger->header().validated; + bool const validated = ledger->header().validated; // Returns true if we already had the ledger - return mLedgerHistory.insert(std::move(ledger), validated); + return mLedgerHistory.insert(ledger, validated); } /** Apply held transactions to the open ledger @@ -418,10 +422,10 @@ void LedgerMaster::applyHeldTransactions() { CanonicalTXSet const set = [this]() { - std::lock_guard sl(m_mutex); + std::lock_guard const sl(m_mutex); // VFALCO NOTE The hash for an open ledger is undefined so we use // something that is a reasonable substitute. - CanonicalTXSet set(app_.openLedger().current()->header().parentHash); + CanonicalTXSet set(app_.getOpenLedger().current()->header().parentHash); std::swap(mHeldTransactions, set); return set; }(); @@ -433,7 +437,7 @@ LedgerMaster::applyHeldTransactions() std::shared_ptr LedgerMaster::popAcctTransaction(std::shared_ptr const& tx) { - std::lock_guard sl(m_mutex); + std::lock_guard const sl(m_mutex); return mHeldTransactions.popAcctTransaction(tx); } @@ -447,14 +451,14 @@ LedgerMaster::setBuildingLedger(LedgerIndex i) bool LedgerMaster::haveLedger(std::uint32_t seq) { - std::lock_guard sl(mCompleteLock); + std::lock_guard const sl(mCompleteLock); return boost::icl::contains(mCompleteLedgers, seq); } void LedgerMaster::clearLedger(std::uint32_t seq) { - std::lock_guard sl(mCompleteLock); + std::lock_guard const sl(mCompleteLock); mCompleteLedgers.erase(seq); } @@ -481,7 +485,7 @@ LedgerMaster::isValidated(ReadView const& ledger) if (hash) { XRPL_ASSERT(hash->isNonZero(), "xrpl::LedgerMaster::isValidated : nonzero hash"); - uint256 valHash = app_.getRelationalDatabase().getHashByIndex(seq); + uint256 const valHash = app_.getRelationalDatabase().getHashByIndex(seq); if (valHash == ledger.header().hash) { // SQL database doesn't match ledger chain @@ -510,19 +514,23 @@ LedgerMaster::getFullValidatedRange(std::uint32_t& minVal, std::uint32_t& maxVal // published ledger which is. maxVal = mPubLedgerSeq.load(); - if (!maxVal) + if (maxVal == 0u) return false; std::optional maybeMin; { - std::lock_guard sl(mCompleteLock); + std::lock_guard const sl(mCompleteLock); maybeMin = prevMissing(mCompleteLedgers, maxVal); } if (maybeMin == std::nullopt) + { minVal = maxVal; + } else + { minVal = 1 + *maybeMin; + } return true; } @@ -537,16 +545,16 @@ LedgerMaster::getValidatedRange(std::uint32_t& minVal, std::uint32_t& maxVal) // Remove from the validated range any ledger sequences that may not be // fully updated in the database yet - auto const pendingSaves = app_.pendingSaves().getSnapshot(); + auto const pendingSaves = app_.getPendingSaves().getSnapshot(); if (!pendingSaves.empty() && ((minVal != 0) || (maxVal != 0))) { // Ensure we shrink the tips as much as possible. If we have 7-9 and // 8,9 are invalid, we don't want to see the 8 and shrink to just 9 // because then we'll have nothing when we could have 7. - while (pendingSaves.count(maxVal) > 0) + while (pendingSaves.contains(maxVal)) --maxVal; - while (pendingSaves.count(minVal) > 0) + while (pendingSaves.contains(minVal)) ++minVal; // Best effort for remaining exclusions @@ -555,9 +563,13 @@ LedgerMaster::getValidatedRange(std::uint32_t& minVal, std::uint32_t& maxVal) if ((v.first >= minVal) && (v.first <= maxVal)) { if (v.first > ((minVal + maxVal) / 2)) + { maxVal = v.first - 1; + } else + { minVal = v.first + 1; + } } } @@ -577,9 +589,13 @@ LedgerMaster::getEarliestFetch() std::uint32_t e = getClosedLedger()->header().seq; if (e > fetch_depth_) + { e -= fetch_depth_; + } else + { e = 0; + } return e; } @@ -598,7 +614,7 @@ LedgerMaster::tryFill(std::shared_ptr ledger) while (!app_.getJobQueue().isStopping() && seq > 0) { { - std::lock_guard ml(m_mutex); + std::lock_guard const ml(m_mutex); minHas = seq; --seq; @@ -614,7 +630,7 @@ LedgerMaster::tryFill(std::shared_ptr ledger) return; { - std::lock_guard ml(mCompleteLock); + std::lock_guard const ml(mCompleteLock); mCompleteLedgers.insert(range(minHas, maxHas)); } maxHas = minHas; @@ -642,11 +658,11 @@ LedgerMaster::tryFill(std::shared_ptr ledger) } { - std::lock_guard ml(mCompleteLock); + std::lock_guard const ml(mCompleteLock); mCompleteLedgers.insert(range(minHas, maxHas)); } { - std::lock_guard ml(m_mutex); + std::lock_guard const ml(m_mutex); mFillInProgress = 0; tryAdvance(); } @@ -671,12 +687,12 @@ LedgerMaster::getFetchPack(LedgerIndex missing, InboundLedger::Reason reason) std::shared_ptr target; { int maxScore = 0; - auto peerList = app_.overlay().getActivePeers(); + auto peerList = app_.getOverlay().getActivePeers(); for (auto const& peer : peerList) { if (peer->hasRange(missing, missing + 1)) { - int score = peer->getScore(true); + int const score = peer->getScore(true); if (!target || (score > maxScore)) { target = peer; @@ -775,7 +791,8 @@ LedgerMaster::setFullLedger( { // Check the SQL database's entry for the sequence before this // ledger, if it's not this ledger's parent, invalidate it - uint256 prevHash = app_.getRelationalDatabase().getHashByIndex(ledger->header().seq - 1); + uint256 const prevHash = + app_.getRelationalDatabase().getHashByIndex(ledger->header().seq - 1); if (prevHash.isNonZero() && prevHash != ledger->header().parentHash) clearLedger(ledger->header().seq - 1); } @@ -783,12 +800,12 @@ LedgerMaster::setFullLedger( pendSaveValidated(app_, ledger, isSynchronous, isCurrent); { - std::lock_guard ml(mCompleteLock); + std::lock_guard const ml(mCompleteLock); mCompleteLedgers.insert(ledger->header().seq); } { - std::lock_guard ml(m_mutex); + std::lock_guard const ml(m_mutex); if (ledger->header().seq > mValidLedgerSeq) setValidLedger(ledger); @@ -833,12 +850,12 @@ LedgerMaster::checkAccept(uint256 const& hash, std::uint32_t seq) if (seq < mValidLedgerSeq) return; - auto validations = app_.validators().negativeUNLFilter( + auto validations = app_.getValidators().negativeUNLFilter( app_.getValidations().getTrustedForLedger(hash, seq)); valCount = validations.size(); - if (valCount >= app_.validators().quorum()) + if (valCount >= app_.getValidators().quorum()) { - std::lock_guard ml(m_mutex); + std::lock_guard const ml(m_mutex); if (seq > mLastValidLedger.second) mLastValidLedger = std::make_pair(hash, seq); } @@ -858,8 +875,8 @@ LedgerMaster::checkAccept(uint256 const& hash, std::uint32_t seq) if ((seq != 0) && (getValidLedgerIndex() == 0)) { // Set peers converged early if we can - if (valCount >= app_.validators().quorum()) - app_.overlay().checkTracking(seq); + if (valCount >= app_.getValidators().quorum()) + app_.getOverlay().checkTracking(seq); } // FIXME: We may not want to fetch a ledger with just one @@ -879,7 +896,7 @@ LedgerMaster::checkAccept(uint256 const& hash, std::uint32_t seq) std::size_t LedgerMaster::getNeededValidations() { - return standalone_ ? 0 : app_.validators().quorum(); + return standalone_ ? 0 : app_.getValidators().quorum(); } void @@ -892,13 +909,13 @@ LedgerMaster::checkAccept(std::shared_ptr const& ledger) // Can we advance the last fully-validated ledger? If so, can we // publish? - std::lock_guard ml(m_mutex); + std::lock_guard const ml(m_mutex); if (ledger->header().seq <= mValidLedgerSeq) return; auto const minVal = getNeededValidations(); - auto validations = app_.validators().negativeUNLFilter( + auto validations = app_.getValidators().negativeUNLFilter( app_.getValidations().getTrustedForLedger(ledger->header().hash, ledger->header().seq)); auto const tvc = validations.size(); if (tvc < minVal) // nothing we can do @@ -927,7 +944,7 @@ LedgerMaster::checkAccept(std::shared_ptr const& ledger) fees.reserve(fees.size() + fees2.size()); std::copy(fees2.begin(), fees2.end(), std::back_inserter(fees)); } - std::uint32_t fee; + std::uint32_t fee = 0; if (!fees.empty()) { std::sort(fees.begin(), fees.end()); @@ -954,15 +971,15 @@ LedgerMaster::checkAccept(std::shared_ptr const& ledger) if (ledger->seq() % 256 == 0) { - // Check if the majority of validators run a higher version rippled + // Check if the majority of validators run a higher version xrpld // software. If so print a warning. // - // Validators include their rippled software version in the validation + // Validators include their xrpld software version in the validation // messages of every (flag - 1) ledger. We wait for one ledger time // before checking the version information to accumulate more validation // messages. - auto currentTime = app_.timeKeeper().now(); + auto currentTime = app_.getTimeKeeper().now(); bool needPrint = false; // The variable upgradeWarningPrevTime_ will be set when and only when @@ -973,28 +990,28 @@ LedgerMaster::checkAccept(std::shared_ptr const& ledger) auto const vals = app_.getValidations().getTrustedForLedger( ledger->header().parentHash, ledger->header().seq - 1); std::size_t higherVersionCount = 0; - std::size_t rippledCount = 0; + std::size_t xrpldCount = 0; for (auto const& v : vals) { if (v->isFieldPresent(sfServerVersion)) { auto version = v->getFieldU64(sfServerVersion); higherVersionCount += BuildInfo::isNewerVersion(version) ? 1 : 0; - rippledCount += BuildInfo::isRippledVersion(version) ? 1 : 0; + xrpldCount += BuildInfo::isXrpldVersion(version) ? 1 : 0; } } // We report only if (1) we have accumulated validation messages // from 90% validators from the UNL, (2) 60% of validators - // running the rippled implementation have higher version numbers, + // running the xrpld implementation have higher version numbers, // and (3) the calculation won't cause divide-by-zero. - if (higherVersionCount > 0 && rippledCount > 0) + if (higherVersionCount > 0 && xrpldCount > 0) { constexpr std::size_t reportingPercent = 90; constexpr std::size_t cutoffPercent = 60; - auto const unlSize{app_.validators().getQuorumKeys().second.size()}; + auto const unlSize{app_.getValidators().getQuorumKeys().second.size()}; needPrint = unlSize > 0 && calculatePercent(vals.size(), unlSize) >= reportingPercent && - calculatePercent(higherVersionCount, rippledCount) >= cutoffPercent; + calculatePercent(higherVersionCount, xrpldCount) >= cutoffPercent; } } // To throttle the warning messages, instead of printing a warning @@ -1038,7 +1055,7 @@ LedgerMaster::consensusBuilt( if (ledger->header().seq <= mValidLedgerSeq) { - auto stream = app_.journal("LedgerConsensus").info(); + auto stream = app_.getJournal("LedgerConsensus").info(); JLOG(stream) << "Consensus built old ledger: " << ledger->header().seq << " <= " << mValidLedgerSeq; return; @@ -1049,7 +1066,7 @@ LedgerMaster::consensusBuilt( if (ledger->header().seq <= mValidLedgerSeq) { - auto stream = app_.journal("LedgerConsensus").debug(); + auto stream = app_.getJournal("LedgerConsensus").debug(); JLOG(stream) << "Consensus ledger fully validated"; return; } @@ -1057,16 +1074,14 @@ LedgerMaster::consensusBuilt( // This ledger cannot be the new fully-validated ledger, but // maybe we saved up validations for some other ledger that can be - auto validations = app_.validators().negativeUNLFilter(app_.getValidations().currentTrusted()); + auto validations = + app_.getValidators().negativeUNLFilter(app_.getValidations().currentTrusted()); // Track validation counts with sequence numbers class valSeq { public: - valSeq() : valCount_(0), ledgerSeq_(0) - { - ; - } + valSeq() = default; void mergeValidation(LedgerIndex seq) @@ -1078,8 +1093,8 @@ LedgerMaster::consensusBuilt( ledgerSeq_ = seq; } - std::size_t valCount_; - LedgerIndex ledgerSeq_; + std::size_t valCount_{0}; + LedgerIndex ledgerSeq_{0}; }; // Count the number of current, trusted validations @@ -1097,6 +1112,7 @@ LedgerMaster::consensusBuilt( // Of the ledgers with sufficient validations, // find the one with the highest sequence for (auto& v : count) + { if (v.second.valCount_ > neededValidations) { // If we still don't know the sequence, get it @@ -1112,10 +1128,11 @@ LedgerMaster::consensusBuilt( maxLedger = v.first; } } + } if (maxSeq > mValidLedgerSeq) { - auto stream = app_.journal("LedgerConsensus").debug(); + auto stream = app_.getJournal("LedgerConsensus").debug(); JLOG(stream) << "Consensus triggered check of ledger"; checkAccept(maxLedger, maxSeq); } @@ -1184,9 +1201,9 @@ LedgerMaster::findNewLedgersToPublish(std::unique_lock& sl auto pubSeq = mPubLedgerSeq + 1; // Next sequence to publish auto valLedger = mValidLedger.get(); - std::uint32_t valSeq = valLedger->header().seq; + std::uint32_t const valSeq = valLedger->header().seq; - scope_unlock sul{sl}; + scope_unlock const sul{sl}; try { for (std::uint32_t seq = pubSeq; seq <= valSeq; ++seq) @@ -1224,8 +1241,10 @@ LedgerMaster::findNewLedgersToPublish(std::unique_lock& sl { // Can we try to acquire the ledger we need? if (!ledger && (++acqCount < ledger_fetch_size_)) + { ledger = app_.getInboundLedgers().acquire( *hash, seq, InboundLedger::Reason::GENERIC); + } } // Did we acquire the next ledger we need to publish? @@ -1284,7 +1303,7 @@ LedgerMaster::findNewLedgersToPublish(std::unique_lock& sl void LedgerMaster::tryAdvance() { - std::lock_guard ml(m_mutex); + std::lock_guard const ml(m_mutex); // Can't advance without at least one fully-valid ledger mAdvanceWork = true; @@ -1319,7 +1338,7 @@ void LedgerMaster::updatePaths() { { - std::lock_guard ml(m_mutex); + std::lock_guard const ml(m_mutex); if (app_.getOPs().isNeedNetworkLedger()) { --mPathFindThread; @@ -1334,7 +1353,7 @@ LedgerMaster::updatePaths() JLOG(m_journal.debug()) << "updatePaths running"; std::shared_ptr lastLedger; { - std::lock_guard ml(m_mutex); + std::lock_guard const ml(m_mutex); if (!mValidLedger.empty() && (!mPathLedger || (mPathLedger->header().seq != mValidLedgerSeq))) @@ -1344,7 +1363,7 @@ LedgerMaster::updatePaths() } else if (mPathFindNewRequest) { // We have a new request but no new ledger - lastLedger = app_.openLedger().current(); + lastLedger = app_.getOpenLedger().current(); } else { // Nothing to do @@ -1358,12 +1377,12 @@ LedgerMaster::updatePaths() if (!standalone_) { // don't pathfind with a ledger that's more than 60 seconds old using namespace std::chrono; - auto age = time_point_cast(app_.timeKeeper().closeTime()) - + auto age = time_point_cast(app_.getTimeKeeper().closeTime()) - lastLedger->header().closeTime; if (age > 1min) { JLOG(m_journal.debug()) << "Published ledger too old for updating paths"; - std::lock_guard ml(m_mutex); + std::lock_guard const ml(m_mutex); --mPathFindThread; mPathLedger.reset(); return; @@ -1372,9 +1391,9 @@ LedgerMaster::updatePaths() try { - auto& pathRequests = app_.getPathRequests(); + auto& pathRequests = app_.getPathRequestManager(); { - std::lock_guard ml(m_mutex); + std::lock_guard const ml(m_mutex); if (!pathRequests.requestsPending()) { --mPathFindThread; @@ -1388,7 +1407,7 @@ LedgerMaster::updatePaths() JLOG(m_journal.debug()) << "Updating paths"; pathRequests.updateAll(lastLedger); - std::lock_guard ml(m_mutex); + std::lock_guard const ml(m_mutex); if (!pathRequests.requestsPending()) { JLOG(m_journal.debug()) << "No path requests left. No need for further updating " @@ -1432,7 +1451,7 @@ LedgerMaster::newPathRequest() bool LedgerMaster::isNewPathRequest() { - std::lock_guard ml(m_mutex); + std::lock_guard const ml(m_mutex); bool const ret = mPathFindNewRequest; mPathFindNewRequest = false; return ret; @@ -1454,7 +1473,7 @@ LedgerMaster::newOrderBookDB() bool LedgerMaster::newPFWork(char const* name, std::unique_lock&) { - if (!app_.isStopping() && mPathFindThread < 2 && app_.getPathRequests().requestsPending()) + if (!app_.isStopping() && mPathFindThread < 2 && app_.getPathRequestManager().requestsPending()) { JLOG(m_journal.debug()) << "newPFWork: Creating job. path find threads: " << mPathFindThread; @@ -1478,7 +1497,7 @@ LedgerMaster::peekMutex() std::shared_ptr LedgerMaster::getCurrentLedger() { - return app_.openLedger().current(); + return app_.getOpenLedger().current(); } std::shared_ptr @@ -1505,14 +1524,14 @@ LedgerMaster::getValidatedRules() std::shared_ptr LedgerMaster::getPublishedLedger() { - std::lock_guard lock(m_mutex); + std::lock_guard const lock(m_mutex); return mPubLedger; } std::string LedgerMaster::getCompleteLedgers() const { - std::lock_guard sl(mCompleteLock); + std::lock_guard const sl(mCompleteLock); return to_string(mCompleteLedgers); } @@ -1541,7 +1560,7 @@ LedgerMaster::missingFromCompleteLedgerRange(LedgerIndex first, LedgerIndex last std::optional LedgerMaster::getCloseTimeBySeq(LedgerIndex ledgerIndex) { - uint256 hash = getHashBySeq(ledgerIndex); + uint256 const hash = getHashBySeq(ledgerIndex); return hash.isNonZero() ? getCloseTimeByHash(hash, ledgerIndex) : std::nullopt; } @@ -1605,7 +1624,7 @@ LedgerMaster::walkHashBySeq( // The hash is not in the reference ledger. Get another ledger which can // be located easily and should contain the hash. - LedgerIndex refIndex = getCandidateLedger(index); + LedgerIndex const refIndex = getCandidateLedger(index); auto const refHash = hashOfSeq(*referenceLedger, refIndex, m_journal); XRPL_ASSERT(refHash, "xrpl::LedgerMaster::walkHashBySeq : found ledger"); if (refHash) @@ -1693,7 +1712,7 @@ LedgerMaster::getLedgerByHash(uint256 const& hash) void LedgerMaster::setLedgerRangePresent(std::uint32_t minV, std::uint32_t maxV) { - std::lock_guard sl(mCompleteLock); + std::lock_guard const sl(mCompleteLock); mCompleteLedgers.insert(range(minV, maxV)); } @@ -1713,7 +1732,7 @@ LedgerMaster::getCacheHitRate() void LedgerMaster::clearPriorLedgers(LedgerIndex seq) { - std::lock_guard sl(mCompleteLock); + std::lock_guard const sl(mCompleteLock); if (seq > 0) mCompleteLedgers.erase(range(0u, seq - 1)); } @@ -1743,7 +1762,7 @@ LedgerMaster::fetchForHistory( InboundLedger::Reason reason, std::unique_lock& sl) { - scope_unlock sul{sl}; + scope_unlock const sul{sl}; if (auto hash = getLedgerHashForHistory(missing, reason)) { XRPL_ASSERT(hash->isNonZero(), "xrpl::LedgerMaster::fetchForHistory : found ledger"); @@ -1772,9 +1791,9 @@ LedgerMaster::fetchForHistory( XRPL_ASSERT(seq == missing, "xrpl::LedgerMaster::fetchForHistory : sequence match"); JLOG(m_journal.trace()) << "fetchForHistory acquired " << seq; setFullLedger(ledger, false, false); - int fillInProgress; + int fillInProgress = 0; { - std::lock_guard lock(m_mutex); + std::lock_guard const lock(m_mutex); mHistLedger = ledger; fillInProgress = mFillInProgress; } @@ -1783,7 +1802,7 @@ LedgerMaster::fetchForHistory( { { // Previous ledger is in DB - std::lock_guard lock(m_mutex); + std::lock_guard const lock(m_mutex); mFillInProgress = seq; } app_.getJobQueue().addJob( @@ -1793,7 +1812,7 @@ LedgerMaster::fetchForHistory( } else { - std::uint32_t fetchSz; + std::uint32_t fetchSz = 0; // Do not fetch ledger sequences lower // than the earliest ledger sequence fetchSz = app_.getNodeStore().earliestLedgerSeq(); @@ -1803,7 +1822,7 @@ LedgerMaster::fetchForHistory( { for (std::uint32_t i = 0; i < fetchSz; ++i) { - std::uint32_t seq = missing - i; + std::uint32_t const seq = missing - i; if (auto h = getLedgerHashForHistory(seq, reason)) { XRPL_ASSERT( @@ -1852,10 +1871,10 @@ LedgerMaster::doAdvance(std::unique_lock& sl) (app_.getNodeStore().getWriteLoad() < MAX_WRITE_LOAD_ACQUIRE)) { // We are in sync, so can acquire - InboundLedger::Reason reason = InboundLedger::Reason::HISTORY; + InboundLedger::Reason const reason = InboundLedger::Reason::HISTORY; std::optional missing; { - std::lock_guard sll(mCompleteLock); + std::lock_guard const sll(mCompleteLock); missing = prevMissing( mCompleteLedgers, mPubLedger->header().seq, @@ -1875,7 +1894,9 @@ LedgerMaster::doAdvance(std::unique_lock& sl) JLOG(m_journal.trace()) << "advanceThread should acquire"; } else + { missing = std::nullopt; + } } if (missing) { @@ -1900,7 +1921,7 @@ LedgerMaster::doAdvance(std::unique_lock& sl) for (auto const& ledger : pubLedgers) { { - scope_unlock sul{sl}; + scope_unlock const sul{sl}; JLOG(m_journal.debug()) << "tryAdvance publishing seq " << ledger->header().seq; setFullLedger(ledger, true, true); } @@ -1908,7 +1929,7 @@ LedgerMaster::doAdvance(std::unique_lock& sl) setPubLedger(ledger); { - scope_unlock sul{sl}; + scope_unlock const sul{sl}; app_.getOPs().pubLedger(ledger); } } @@ -2089,7 +2110,7 @@ LedgerMaster::makeFetchPack( // the same process adding the previous ledger to the FetchPack. do { - std::uint32_t lSeq = want->header().seq; + std::uint32_t const lSeq = want->header().seq; { // Serialize the ledger header: @@ -2158,9 +2179,11 @@ LedgerMaster::txnIdFromIndex(uint32_t ledgerSeq, uint32_t txnIndex) return {}; for (auto it = lgr->txs.begin(); it != lgr->txs.end(); ++it) + { if (it->first && it->second && it->second->isFieldPresent(sfTransactionIndex) && it->second->getFieldU32(sfTransactionIndex) == txnIndex) return it->first->getTransactionID(); + } return {}; } diff --git a/src/xrpld/app/ledger/detail/LedgerPersistence.cpp b/src/xrpld/app/ledger/detail/LedgerPersistence.cpp new file mode 100644 index 0000000000..91de010f1d --- /dev/null +++ b/src/xrpld/app/ledger/detail/LedgerPersistence.cpp @@ -0,0 +1,172 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +static bool +saveValidatedLedger( + ServiceRegistry& registry, + std::shared_ptr const& ledger, + bool current) +{ + auto j = registry.getJournal("Ledger"); + auto seq = ledger->header().seq; + if (!registry.getPendingSaves().startWork(seq)) + { + // The save was completed synchronously + JLOG(j.debug()) << "Save aborted"; + return true; + } + + auto& db = registry.getRelationalDatabase(); + + auto const res = db.saveValidatedLedger(ledger, current); + + // Clients can now trust the database for + // information about this ledger sequence. + registry.getPendingSaves().finishWork(seq); + return res; +} + +bool +pendSaveValidated( + ServiceRegistry& registry, + std::shared_ptr const& ledger, + bool isSynchronous, + bool isCurrent) +{ + if (!registry.getHashRouter().setFlags(ledger->header().hash, HashRouterFlags::SAVED)) + { + // We have tried to save this ledger recently + auto stream = registry.getJournal("Ledger").debug(); + JLOG(stream) << "Double pend save for " << ledger->header().seq; + + if (!isSynchronous || !registry.getPendingSaves().pending(ledger->header().seq)) + { + // Either we don't need it to be finished + // or it is finished + return true; + } + } + + XRPL_ASSERT(ledger->isImmutable(), "xrpl::pendSaveValidated : immutable ledger"); + + if (!registry.getPendingSaves().shouldWork(ledger->header().seq, isSynchronous)) + { + auto stream = registry.getJournal("Ledger").debug(); + JLOG(stream) << "Pend save with seq in pending saves " << ledger->header().seq; + + return true; + } + + // See if we can use the JobQueue. + if (!isSynchronous && + registry.getJobQueue().addJob( + isCurrent ? jtPUBLEDGER : jtPUBOLDLEDGER, + "Pub" + std::to_string(ledger->seq()), + [®istry, ledger, isCurrent]() { saveValidatedLedger(registry, ledger, isCurrent); })) + { + return true; + } + + // The JobQueue won't do the Job. Do the save synchronously. + return saveValidatedLedger(registry, ledger, isCurrent); +} + +std::shared_ptr +loadLedgerHelper( + LedgerHeader const& info, + Rules const& rules, + Fees const& fees, + ServiceRegistry& registry, + bool acquire) +{ + bool loaded = false; + auto ledger = std::make_shared( + info, + loaded, + acquire, + rules, + fees, + registry.getNodeFamily(), + registry.getJournal("Ledger")); + + if (!loaded) + ledger.reset(); + + return ledger; +} + +static void +finishLoadByIndexOrHash(std::shared_ptr const& ledger, beast::Journal j) +{ + if (!ledger) + return; + + XRPL_ASSERT( + ledger->header().seq < XRP_LEDGER_EARLIEST_FEES || ledger->read(keylet::fees()), + "xrpl::finishLoadByIndexOrHash : valid ledger fees"); + ledger->setImmutable(); + + JLOG(j.trace()) << "Loaded ledger: " << to_string(ledger->header().hash); + + ledger->setFull(); +} + +std::tuple, std::uint32_t, uint256> +getLatestLedger(Rules const& rules, Fees const& fees, ServiceRegistry& registry) +{ + std::optional const info = registry.getRelationalDatabase().getNewestLedgerInfo(); + if (!info) + return {std::shared_ptr(), {}, {}}; + return {loadLedgerHelper(*info, rules, fees, registry, true), info->seq, info->hash}; +} + +std::shared_ptr +loadByIndex( + std::uint32_t ledgerIndex, + Rules const& rules, + Fees const& fees, + ServiceRegistry& registry, + bool acquire) +{ + if (std::optional info = + registry.getRelationalDatabase().getLedgerInfoByIndex(ledgerIndex)) + { + std::shared_ptr ledger = loadLedgerHelper(*info, rules, fees, registry, acquire); + finishLoadByIndexOrHash(ledger, registry.getJournal("Ledger")); + return ledger; + } + return {}; +} + +std::shared_ptr +loadByHash( + uint256 const& ledgerHash, + Rules const& rules, + Fees const& fees, + ServiceRegistry& registry, + bool acquire) +{ + if (std::optional info = + registry.getRelationalDatabase().getLedgerInfoByHash(ledgerHash)) + { + std::shared_ptr ledger = loadLedgerHelper(*info, rules, fees, registry, acquire); + finishLoadByIndexOrHash(ledger, registry.getJournal("Ledger")); + XRPL_ASSERT( + !ledger || ledger->header().hash == ledgerHash, + "xrpl::loadByHash : ledger hash match if loaded"); + return ledger; + } + return {}; +} + +} // namespace xrpl diff --git a/src/xrpld/app/ledger/detail/LedgerReplay.cpp b/src/xrpld/app/ledger/detail/LedgerReplay.cpp index d115402115..a02267e4a1 100644 --- a/src/xrpld/app/ledger/detail/LedgerReplay.cpp +++ b/src/xrpld/app/ledger/detail/LedgerReplay.cpp @@ -1,6 +1,7 @@ -#include #include +#include + namespace xrpl { LedgerReplay::LedgerReplay( diff --git a/src/xrpld/app/ledger/detail/LedgerReplayMsgHandler.cpp b/src/xrpld/app/ledger/detail/LedgerReplayMsgHandler.cpp index 73fcf77e4e..93d7ac0d2f 100644 --- a/src/xrpld/app/ledger/detail/LedgerReplayMsgHandler.cpp +++ b/src/xrpld/app/ledger/detail/LedgerReplayMsgHandler.cpp @@ -3,13 +3,14 @@ #include #include +#include #include #include namespace xrpl { LedgerReplayMsgHandler::LedgerReplayMsgHandler(Application& app, LedgerReplayer& replayer) - : app_(app), replayer_(replayer), journal_(app.journal("LedgerReplayMsgHandler")) + : app_(app), replayer_(replayer), journal_(app.getJournal("LedgerReplayMsgHandler")) { } @@ -81,7 +82,7 @@ bool LedgerReplayMsgHandler::processProofPathResponse( std::shared_ptr const& msg) { - protocol::TMProofPathResponse& reply = *msg; + protocol::TMProofPathResponse const& reply = *msg; if (reply.has_error() || !reply.has_key() || !reply.has_ledgerhash() || !reply.has_type() || !reply.has_ledgerheader() || reply.path_size() == 0) { @@ -97,7 +98,7 @@ LedgerReplayMsgHandler::processProofPathResponse( // deserialize the header auto info = deserializeHeader({reply.ledgerheader().data(), reply.ledgerheader().size()}); - uint256 replyHash(reply.ledgerhash()); + uint256 const replyHash(reply.ledgerhash()); if (calculateLedgerHash(info) != replyHash) { JLOG(journal_.debug()) << "Bad message: Hash mismatch"; @@ -105,7 +106,7 @@ LedgerReplayMsgHandler::processProofPathResponse( } info.hash = replyHash; - uint256 key(reply.key()); + uint256 const key(reply.key()); if (key != keylet::skip().key) { JLOG(journal_.debug()) << "Bad message: we only support the short skip list for now. " @@ -136,7 +137,7 @@ LedgerReplayMsgHandler::processProofPathResponse( return false; } - if (auto item = static_cast(node.get())->peekItem()) + if (auto item = safe_downcast(node.get())->peekItem()) { replayer_.gotSkipList(info, item); return true; @@ -150,7 +151,7 @@ protocol::TMReplayDeltaResponse LedgerReplayMsgHandler::processReplayDeltaRequest( std::shared_ptr const& msg) { - protocol::TMReplayDeltaRequest& packet = *msg; + protocol::TMReplayDeltaRequest const& packet = *msg; protocol::TMReplayDeltaResponse reply; if (!packet.has_ledgerhash() || packet.ledgerhash().size() != uint256::size()) @@ -189,7 +190,7 @@ bool LedgerReplayMsgHandler::processReplayDeltaResponse( std::shared_ptr const& msg) { - protocol::TMReplayDeltaResponse& reply = *msg; + protocol::TMReplayDeltaResponse const& reply = *msg; if (reply.has_error() || !reply.has_ledgerheader()) { JLOG(journal_.debug()) << "Bad message: Error reply"; @@ -197,7 +198,7 @@ LedgerReplayMsgHandler::processReplayDeltaResponse( } auto info = deserializeHeader({reply.ledgerheader().data(), reply.ledgerheader().size()}); - uint256 replyHash(reply.ledgerhash()); + uint256 const replyHash(reply.ledgerhash()); if (calculateLedgerHash(info) != replyHash) { JLOG(journal_.debug()) << "Bad message: Hash mismatch"; @@ -216,7 +217,8 @@ LedgerReplayMsgHandler::processReplayDeltaResponse( // -- TxShaMapItem for building a ShaMap for verification // -- Tx // -- TxMetaData for Tx ordering - Serializer shaMapItemData(reply.transaction(i).data(), reply.transaction(i).size()); + Serializer const shaMapItemData( + reply.transaction(i).data(), reply.transaction(i).size()); SerialIter txMetaSit(makeSlice(reply.transaction(i))); SerialIter txSit(txMetaSit.getSlice(txMetaSit.getVLDataLength())); diff --git a/src/xrpld/app/ledger/detail/LedgerReplayTask.cpp b/src/xrpld/app/ledger/detail/LedgerReplayTask.cpp index 42db911830..f393c7fca8 100644 --- a/src/xrpld/app/ledger/detail/LedgerReplayTask.cpp +++ b/src/xrpld/app/ledger/detail/LedgerReplayTask.cpp @@ -67,13 +67,13 @@ LedgerReplayTask::LedgerReplayTask( InboundLedgers& inboundLedgers, LedgerReplayer& replayer, std::shared_ptr& skipListAcquirer, - TaskParameter&& parameter) + TaskParameter const& parameter) : TimeoutCounter( app, parameter.finishHash_, LedgerReplayParameters::TASK_TIMEOUT, {jtREPLAY_TASK, "LedReplTask", LedgerReplayParameters::MAX_QUEUED_TASKS}, - app.journal("LedgerReplayTask")) + app.getJournal("LedgerReplayTask")) , inboundLedgers_(inboundLedgers) , replayer_(replayer) , parameter_(parameter) @@ -96,7 +96,7 @@ LedgerReplayTask::init() { JLOG(journal_.debug()) << "Task start " << hash_; - std::weak_ptr wptr = shared_from_this(); + std::weak_ptr const wptr = shared_from_this(); skipListAcquirer_->addDataCallback([wptr](bool good, uint256 const& hash) { if (auto sptr = wptr.lock(); sptr) { @@ -163,7 +163,8 @@ LedgerReplayTask::tryAdvance(ScopedLockType& sl) << ", deltaIndex=" << deltaToBuild_ << ", totalDeltas=" << deltas_.size() << ", parent " << (parent_ ? parent_->header().hash : uint256()); - bool shouldTry = parent_ && parameter_.full_ && parameter_.totalLedgers_ - 1 == deltas_.size(); + bool const shouldTry = + parent_ && parameter_.full_ && parameter_.totalLedgers_ - 1 == deltas_.size(); if (!shouldTry) return; @@ -183,7 +184,9 @@ LedgerReplayTask::tryAdvance(ScopedLockType& sl) parent_ = l; } else + { return; + } } complete_ = true; @@ -202,7 +205,7 @@ LedgerReplayTask::updateSkipList( std::vector const& sList) { { - ScopedLockType sl(mtx_); + ScopedLockType const sl(mtx_); if (isDone()) return; if (!parameter_.update(hash, seq, sList)) @@ -243,18 +246,22 @@ LedgerReplayTask::pmDowncast() void LedgerReplayTask::addDelta(std::shared_ptr const& delta) { - std::weak_ptr wptr = shared_from_this(); + std::weak_ptr const wptr = shared_from_this(); delta->addDataCallback(parameter_.reason_, [wptr](bool good, uint256 const& hash) { if (auto sptr = wptr.lock(); sptr) { if (!good) + { sptr->cancel(); + } else + { sptr->deltaReady(hash); + } } }); - ScopedLockType sl(mtx_); + ScopedLockType const sl(mtx_); if (!isDone()) { JLOG(journal_.trace()) << "addDelta task " << hash_ << " deltaIndex=" << deltaToBuild_ @@ -270,7 +277,7 @@ LedgerReplayTask::addDelta(std::shared_ptr const& delta) bool LedgerReplayTask::finished() const { - ScopedLockType sl(mtx_); + ScopedLockType const sl(mtx_); return isDone(); } diff --git a/src/xrpld/app/ledger/detail/LedgerReplayer.cpp b/src/xrpld/app/ledger/detail/LedgerReplayer.cpp index f649eb4d95..ae3552f258 100644 --- a/src/xrpld/app/ledger/detail/LedgerReplayer.cpp +++ b/src/xrpld/app/ledger/detail/LedgerReplayer.cpp @@ -11,13 +11,13 @@ LedgerReplayer::LedgerReplayer( : app_(app) , inboundLedgers_(inboundLedgers) , peerSetBuilder_(std::move(peerSetBuilder)) - , j_(app.journal("LedgerReplayer")) + , j_(app.getJournal("LedgerReplayer")) { } LedgerReplayer::~LedgerReplayer() { - std::lock_guard lock(mtx_); + std::lock_guard const lock(mtx_); tasks_.clear(); } @@ -32,13 +32,14 @@ LedgerReplayer::replay( totalNumLedgers <= LedgerReplayParameters::MAX_TASK_SIZE, "xrpl::LedgerReplayer::replay : valid inputs"); + // NOLINTNEXTLINE(misc-const-correctness) LedgerReplayTask::TaskParameter parameter(r, finishLedgerHash, totalNumLedgers); std::shared_ptr task; std::shared_ptr skipList; bool newSkipList = false; { - std::lock_guard lock(mtx_); + std::lock_guard const lock(mtx_); if (app_.isStopping()) return; if (tasks_.size() >= LedgerReplayParameters::MAX_TASKS) @@ -98,8 +99,12 @@ LedgerReplayer::createDeltas(std::shared_ptr task) { auto skipListItem = std::find(parameter.skipList_.begin(), parameter.skipList_.end(), parameter.startHash_); - if (skipListItem == parameter.skipList_.end() || - ++skipListItem == parameter.skipList_.end()) + auto const wasLast = skipListItem == parameter.skipList_.end(); + if (not wasLast) + ++skipListItem; + auto const isLast = skipListItem == parameter.skipList_.end(); + + if (wasLast || isLast) { JLOG(j_.error()) << "Task parameter error when creating deltas " << parameter.finishHash_; @@ -113,7 +118,7 @@ LedgerReplayer::createDeltas(std::shared_ptr task) std::shared_ptr delta; bool newDelta = false; { - std::lock_guard lock(mtx_); + std::lock_guard const lock(mtx_); if (app_.isStopping()) return; auto i = deltas_.find(*skipListItem); @@ -143,7 +148,7 @@ LedgerReplayer::gotSkipList( { std::shared_ptr skipList = {}; { - std::lock_guard lock(mtx_); + std::lock_guard const lock(mtx_); auto i = skipLists_.find(info.hash); if (i == skipLists_.end()) return; @@ -166,7 +171,7 @@ LedgerReplayer::gotReplayDelta( { std::shared_ptr delta = {}; { - std::lock_guard lock(mtx_); + std::lock_guard const lock(mtx_); auto i = deltas_.find(info.hash); if (i == deltas_.end()) return; @@ -187,7 +192,7 @@ LedgerReplayer::sweep() { auto const start = std::chrono::steady_clock::now(); { - std::lock_guard lock(mtx_); + std::lock_guard const lock(mtx_); JLOG(j_.debug()) << "Sweeping, LedgerReplayer has " << tasks_.size() << " tasks, " << skipLists_.size() << " skipLists, and " << deltas_.size() << " deltas."; @@ -213,7 +218,9 @@ LedgerReplayer::sweep() it = subTasks.erase(it); } else + { ++it; + } } }; removeCannotLocked(skipLists_); @@ -231,7 +238,7 @@ LedgerReplayer::stop() { JLOG(j_.info()) << "Stopping..."; { - std::lock_guard lock(mtx_); + std::lock_guard const lock(mtx_); std::for_each(tasks_.begin(), tasks_.end(), [](auto& i) { i->cancel(); }); tasks_.clear(); auto lockAndCancel = [](auto& i) { diff --git a/src/xrpld/app/ledger/detail/LedgerToJson.cpp b/src/xrpld/app/ledger/detail/LedgerToJson.cpp index 038a5ae742..a48756f9b6 100644 --- a/src/xrpld/app/ledger/detail/LedgerToJson.cpp +++ b/src/xrpld/app/ledger/detail/LedgerToJson.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -17,19 +18,19 @@ namespace { bool isFull(LedgerFill const& fill) { - return fill.options & LedgerFill::full; + return (fill.options & LedgerFill::full) != 0; } bool isExpanded(LedgerFill const& fill) { - return isFull(fill) || (fill.options & LedgerFill::expand); + return isFull(fill) || ((fill.options & LedgerFill::expand) != 0); } bool isBinary(LedgerFill const& fill) { - return fill.options & LedgerFill::binary; + return (fill.options & LedgerFill::binary) != 0; } void @@ -74,7 +75,9 @@ void fillJsonBinary(Json::Value& json, bool closed, LedgerHeader const& info) { if (!closed) + { json[jss::closed] = false; + } else { json[jss::closed] = true; @@ -120,11 +123,13 @@ fillJsonTx( // If applicable, insert delivered amount if (txnType == ttPAYMENT || txnType == ttCHECK_CASH) + { RPC::insertDeliveredAmount( txJson[jss::meta], fill.ledger, txn, {txn->getTransactionID(), fill.ledger.seq(), *stMeta}); + } // If applicable, insert mpt issuance id RPC::insertMPTokenIssuanceID( @@ -154,11 +159,13 @@ fillJsonTx( // If applicable, insert delivered amount if (txnType == ttPAYMENT || txnType == ttCHECK_CASH) + { RPC::insertDeliveredAmount( txJson[jss::metaData], fill.ledger, txn, {txn->getTransactionID(), fill.ledger.seq(), *stMeta}); + } // If applicable, insert mpt issuance id RPC::insertMPTokenIssuanceID( @@ -166,7 +173,7 @@ fillJsonTx( } } - if ((fill.options & LedgerFill::ownerFunds) && txn->getTxnType() == ttOFFER_CREATE) + if (((fill.options & LedgerFill::ownerFunds) != 0) && txn->getTxnType() == ttOFFER_CREATE) { auto const account = txn->getAccountID(sfAccount); auto const amount = txn->getFieldAmount(sfTakerGets); @@ -209,7 +216,7 @@ fillJsonTx(Json::Value& json, LedgerFill const& fill) catch (std::exception const& ex) { // Nothing the user can do about this. - if (fill.context) + if (fill.context != nullptr) { JLOG(fill.context->j.error()) << "Exception in " << __func__ << ": " << ex.what(); } @@ -233,9 +240,13 @@ fillJsonState(Json::Value& json, LedgerFill const& fill) obj[jss::tx_blob] = serializeHex(*sle); } else if (expanded) + { array.append(sle->getJson(JsonOptions::none)); + } else + { array.append(to_string(sle->key())); + } } } @@ -266,9 +277,13 @@ fillJsonQueue(Json::Value& json, LedgerFill const& fill) auto&& temp = fillJsonTx(fill, bBinary, bExpanded, tx.txn, nullptr); if (fill.context->apiVersion > 1) + { copyFrom(txJson, temp); + } else + { copyFrom(txJson[jss::tx], temp); + } } } @@ -279,19 +294,24 @@ fillJson(Json::Value& json, LedgerFill const& fill) // Is there a way to report this back? auto bFull = isFull(fill); if (isBinary(fill)) + { fillJsonBinary(json, !fill.ledger.open(), fill.ledger.header()); + } else + { fillJson( json, !fill.ledger.open(), fill.ledger.header(), bFull, - (fill.context ? fill.context->apiVersion : RPC::apiMaximumSupportedVersion)); + ((fill.context != nullptr) ? fill.context->apiVersion + : RPC::apiMaximumSupportedVersion)); + } - if (bFull || fill.options & LedgerFill::dumpTxrp) + if (bFull || ((fill.options & LedgerFill::dumpTxrp) != 0)) fillJsonTx(json, fill); - if (bFull || fill.options & LedgerFill::dumpState) + if (bFull || ((fill.options & LedgerFill::dumpState) != 0)) fillJsonState(json, fill); } @@ -303,7 +323,7 @@ addJson(Json::Value& json, LedgerFill const& fill) auto& object = json[jss::ledger] = Json::objectValue; fillJson(object, fill); - if ((fill.options & LedgerFill::dumpQueue) && !fill.txQueue.empty()) + if (((fill.options & LedgerFill::dumpQueue) != 0) && !fill.txQueue.empty()) fillJsonQueue(json, fill); } @@ -318,8 +338,10 @@ getJson(LedgerFill const& fill) void copyFrom(Json::Value& to, Json::Value const& from) { - if (!to) // Short circuit this very common case. + if (!to) + { // Short circuit this very common case. to = from; + } else { // TODO: figure out if there is a way to remove this clause diff --git a/src/xrpld/app/ledger/detail/LocalTxs.cpp b/src/xrpld/app/ledger/detail/LocalTxs.cpp index 48bd80fbf2..38969a092c 100644 --- a/src/xrpld/app/ledger/detail/LocalTxs.cpp +++ b/src/xrpld/app/ledger/detail/LocalTxs.cpp @@ -1,6 +1,6 @@ -#include #include +#include #include /* @@ -94,7 +94,7 @@ public: void push_back(LedgerIndex index, std::shared_ptr const& txn) override { - std::lock_guard lock(m_lock); + std::lock_guard const lock(m_lock); m_txns.emplace_back(index, txn); } @@ -107,7 +107,7 @@ public: // Get the set of local transactions as a canonical // set (so they apply in a valid order) { - std::lock_guard lock(m_lock); + std::lock_guard const lock(m_lock); for (auto const& it : m_txns) tset.insert(it.getTX()); @@ -121,7 +121,7 @@ public: void sweep(ReadView const& view) override { - std::lock_guard lock(m_lock); + std::lock_guard const lock(m_lock); m_txns.remove_if([&view](auto const& txn) { if (txn.isExpired(view.header().seq)) @@ -142,10 +142,12 @@ public: return acctSeq > seqProx; // Remove tefPAST_SEQ if (seqProx.isTicket() && acctSeq.value() <= seqProx.value()) + { // Keep ticket from the future. Note, however, that the // transaction will not be held indefinitely since LocalTxs // will only hold a transaction for a maximum of 5 ledgers. return false; + } // Ticket should have been created by now. Remove if ticket // does not exist. @@ -156,7 +158,7 @@ public: std::size_t size() override { - std::lock_guard lock(m_lock); + std::lock_guard const lock(m_lock); return m_txns.size(); } diff --git a/src/xrpld/app/ledger/detail/OpenLedger.cpp b/src/xrpld/app/ledger/detail/OpenLedger.cpp index a26751bc64..dfce2278a5 100644 --- a/src/xrpld/app/ledger/detail/OpenLedger.cpp +++ b/src/xrpld/app/ledger/detail/OpenLedger.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -24,26 +25,26 @@ OpenLedger::OpenLedger( bool OpenLedger::empty() const { - std::lock_guard lock(modify_mutex_); + std::lock_guard const lock(modify_mutex_); return current_->txCount() == 0; } std::shared_ptr OpenLedger::current() const { - std::lock_guard lock(current_mutex_); + std::lock_guard const lock(current_mutex_); return current_; } bool OpenLedger::modify(modify_type const& f) { - std::lock_guard lock1(modify_mutex_); + std::lock_guard const lock1(modify_mutex_); auto next = std::make_shared(*current_); auto const changed = f(*next, j_); if (changed) { - std::lock_guard lock2(current_mutex_); + std::lock_guard const lock2(current_mutex_); current_ = std::move(next); } return changed; @@ -72,7 +73,7 @@ OpenLedger::accept( // Block calls to modify, otherwise // new tx going into the open ledger // would get lost. - std::lock_guard lock1(modify_mutex_); + std::lock_guard const lock1(modify_mutex_); // Apply tx from the current open view if (!current_->txs.empty()) { @@ -124,13 +125,13 @@ OpenLedger::accept( tx->add(s); msg.set_rawtransaction(s.data(), s.size()); msg.set_status(protocol::tsNEW); - msg.set_receivetimestamp(app.timeKeeper().now().time_since_epoch().count()); - app.overlay().relay(txId, msg, *toSkip); + msg.set_receivetimestamp(app.getTimeKeeper().now().time_since_epoch().count()); + app.getOverlay().relay(txId, msg, *toSkip); } } // Switch to the new open view - std::lock_guard lock2(current_mutex_); + std::lock_guard const lock2(current_mutex_); current_ = std::move(next); } diff --git a/src/xrpld/app/ledger/detail/SkipListAcquire.cpp b/src/xrpld/app/ledger/detail/SkipListAcquire.cpp index 2191ef965a..20d63bcb64 100644 --- a/src/xrpld/app/ledger/detail/SkipListAcquire.cpp +++ b/src/xrpld/app/ledger/detail/SkipListAcquire.cpp @@ -16,7 +16,7 @@ SkipListAcquire::SkipListAcquire( ledgerHash, LedgerReplayParameters::SUB_TASK_TIMEOUT, {jtREPLAY_TASK, "SkipListAcq", LedgerReplayParameters::MAX_QUEUED_TASKS}, - app.journal("LedgerReplaySkipList")) + app.getJournal("LedgerReplaySkipList")) , inboundLedgers_(inboundLedgers) , peerSet_(std::move(peerSet)) { @@ -151,7 +151,7 @@ SkipListAcquire::addDataCallback(OnSkipListDataCB&& cb) std::shared_ptr SkipListAcquire::getData() const { - ScopedLockType sl(mtx_); + ScopedLockType const sl(mtx_); return data_; } diff --git a/src/xrpld/app/ledger/detail/SkipListAcquire.h b/src/xrpld/app/ledger/detail/SkipListAcquire.h index 5a79e8ae5c..da62578d41 100644 --- a/src/xrpld/app/ledger/detail/SkipListAcquire.h +++ b/src/xrpld/app/ledger/detail/SkipListAcquire.h @@ -1,10 +1,10 @@ #pragma once #include -#include #include #include +#include #include namespace xrpl { diff --git a/src/xrpld/app/ledger/detail/TimeoutCounter.cpp b/src/xrpld/app/ledger/detail/TimeoutCounter.cpp index e2d4409717..216771e60d 100644 --- a/src/xrpld/app/ledger/detail/TimeoutCounter.cpp +++ b/src/xrpld/app/ledger/detail/TimeoutCounter.cpp @@ -15,10 +15,6 @@ TimeoutCounter::TimeoutCounter( : app_(app) , journal_(journal) , hash_(hash) - , timeouts_(0) - , complete_(false) - , failed_(false) - , progress_(false) , timerInterval_(interval) , queueJobParameter_(std::move(jobParameter)) , timer_(app_.getIOContext()) @@ -96,7 +92,7 @@ TimeoutCounter::invokeOnTimer() void TimeoutCounter::cancel() { - ScopedLockType sl(mtx_); + ScopedLockType const sl(mtx_); if (!isDone()) { failed_ = true; diff --git a/src/xrpld/app/ledger/detail/TimeoutCounter.h b/src/xrpld/app/ledger/detail/TimeoutCounter.h index 5f5ccb2f43..a7e4c043be 100644 --- a/src/xrpld/app/ledger/detail/TimeoutCounter.h +++ b/src/xrpld/app/ledger/detail/TimeoutCounter.h @@ -59,6 +59,8 @@ public: virtual void cancel(); + virtual ~TimeoutCounter() = default; + protected: using ScopedLockType = std::unique_lock; @@ -76,8 +78,6 @@ protected: QueueJobParameter&& jobParameter, beast::Journal journal); - virtual ~TimeoutCounter() = default; - /** Schedule a call to queueJob() after mTimerInterval. */ void setTimer(ScopedLockType&); @@ -109,11 +109,11 @@ protected: /** The hash of the object (in practice, always a ledger) we are trying to * fetch. */ uint256 const hash_; - int timeouts_; - bool complete_; - bool failed_; + int timeouts_{0}; + bool complete_{false}; + bool failed_{false}; /** Whether forward progress has been made. */ - bool progress_; + bool progress_{false}; /** The minimum time to wait between calls to execute(). */ std::chrono::milliseconds timerInterval_; diff --git a/src/xrpld/app/ledger/detail/TransactionAcquire.cpp b/src/xrpld/app/ledger/detail/TransactionAcquire.cpp index 8cc26b1da6..d2561718a3 100644 --- a/src/xrpld/app/ledger/detail/TransactionAcquire.cpp +++ b/src/xrpld/app/ledger/detail/TransactionAcquire.cpp @@ -6,6 +6,7 @@ #include +#include #include namespace xrpl { @@ -29,8 +30,7 @@ TransactionAcquire::TransactionAcquire( hash, TX_ACQUIRE_TIMEOUT, {jtTXN_DATA, "TxAcq", {}}, - app.journal("TransactionAcquire")) - , mHaveRoot(false) + app.getJournal("TransactionAcquire")) , mPeerSet(std::move(peerSet)) { mMap = std::make_shared(SHAMapType::TRANSACTION, hash, app_.getNodeFamily()); @@ -129,9 +129,13 @@ TransactionAcquire::trigger(std::shared_ptr const& peer) if (nodes.empty()) { if (mMap->isValid()) + { complete_ = true; + } else + { failed_ = true; + } done(); return; @@ -157,7 +161,7 @@ TransactionAcquire::takeNodes( std::vector> const& data, std::shared_ptr const& peer) { - ScopedLockType sl(mtx_); + ScopedLockType const sl(mtx_); if (complete_) { @@ -183,13 +187,17 @@ TransactionAcquire::takeNodes( if (d.first.isRoot()) { if (mHaveRoot) + { JLOG(journal_.debug()) << "Got root TXS node, already have it"; + } else if (!mMap->addRootNode(SHAMapHash{hash_}, d.second, nullptr).isGood()) { JLOG(journal_.warn()) << "TX acquire got bad root node"; } else + { mHaveRoot = true; + } } else if (!mMap->addKnownNode(d.first, d.second, &sf).isGood()) { @@ -232,10 +240,9 @@ TransactionAcquire::init(int numPeers) void TransactionAcquire::stillNeed() { - ScopedLockType sl(mtx_); + ScopedLockType const sl(mtx_); - if (timeouts_ > NORM_TIMEOUTS) - timeouts_ = NORM_TIMEOUTS; + timeouts_ = std::min(timeouts_, NORM_TIMEOUTS); failed_ = false; } diff --git a/src/xrpld/app/ledger/detail/TransactionAcquire.h b/src/xrpld/app/ledger/detail/TransactionAcquire.h index a39f6a2f35..f29a01fca4 100644 --- a/src/xrpld/app/ledger/detail/TransactionAcquire.h +++ b/src/xrpld/app/ledger/detail/TransactionAcquire.h @@ -31,7 +31,7 @@ public: private: std::shared_ptr mMap; - bool mHaveRoot; + bool mHaveRoot{false}; std::unique_ptr mPeerSet; void diff --git a/src/xrpld/app/ledger/detail/TransactionMaster.cpp b/src/xrpld/app/ledger/detail/TransactionMaster.cpp index b45dbd5ba9..4be3c95993 100644 --- a/src/xrpld/app/ledger/detail/TransactionMaster.cpp +++ b/src/xrpld/app/ledger/detail/TransactionMaster.cpp @@ -15,7 +15,7 @@ TransactionMaster::TransactionMaster(Application& app) 65536, std::chrono::minutes{30}, stopwatch(), - mApp.journal("TaggedCache")) + mApp.getJournal("TaggedCache")) { } @@ -110,7 +110,7 @@ TransactionMaster::fetch( } else { - if (uCommitLedger) + if (uCommitLedger != 0u) iTx->setStatus(COMMITTED, uCommitLedger); txn = iTx->getSTransaction(); diff --git a/src/xrpld/app/main/Application.cpp b/src/xrpld/app/main/Application.cpp index 3e3d87dcd5..129bab8e20 100644 --- a/src/xrpld/app/main/Application.cpp +++ b/src/xrpld/app/main/Application.cpp @@ -3,11 +3,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -21,13 +21,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -92,7 +93,7 @@ private: beast::Journal journal, std::chrono::milliseconds interval, boost::asio::io_context& ios) - : m_event(ev), m_journal(journal), m_probe(interval, ios), lastSample_{} + : m_event(ev), m_journal(journal), m_probe(interval, ios) { } @@ -170,7 +171,7 @@ public: std::unique_ptr m_nodeStore; NodeFamily nodeFamily_; std::unique_ptr m_orderBookDB; - std::unique_ptr m_pathRequests; + std::unique_ptr m_pathRequestManager; std::unique_ptr m_ledgerMaster; std::unique_ptr ledgerCleaner_; std::unique_ptr m_inboundLedgers; @@ -278,11 +279,17 @@ public: // 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) + { count = 6 + std::min(count, 8); + } else if (config->NODE_SIZE >= 3 && count >= 8) + { count = 4 + std::min(count, 6); + } else + { count = 2 + std::min(count, 4); + } return count; }(config_), @@ -324,8 +331,8 @@ public: , m_orderBookDB(make_OrderBookDB(*this, {config_->PATH_SEARCH_MAX, config_->standalone()})) - , m_pathRequests( - std::make_unique( + , m_pathRequestManager( + std::make_unique( *this, logs_->journal("PathRequest"), m_collectorManager->collector())) @@ -482,7 +489,7 @@ public: } Logs& - logs() override + getLogs() override { return *logs_; } @@ -506,7 +513,7 @@ public: } TimeKeeper& - timeKeeper() override + getTimeKeeper() override { return *timeKeeper_; } @@ -600,7 +607,7 @@ public: } void - gotTXSet(std::shared_ptr const& set, bool fromAcquire) + gotTXSet(std::shared_ptr const& set, bool fromAcquire) const { if (set) m_networkOPs->mapComplete(set, fromAcquire); @@ -654,14 +661,14 @@ public: return *m_orderBookDB; } - PathRequests& - getPathRequests() override + PathRequestManager& + getPathRequestManager() override { - return *m_pathRequests; + return *m_pathRequestManager; } CachedSLEs& - cachedSLEs() override + getCachedSLEs() override { return cachedSLEs_; } @@ -697,37 +704,37 @@ public: } ValidatorList& - validators() override + getValidators() override { return *validators_; } ValidatorSite& - validatorSites() override + getValidatorSites() override { return *validatorSites_; } ManifestCache& - validatorManifests() override + getValidatorManifests() override { return *validatorManifests_; } ManifestCache& - publisherManifests() override + getPublisherManifests() override { return *publisherManifests_; } Cluster& - cluster() override + getCluster() override { return *cluster_; } PeerReservationTable& - peerReservations() override + getPeerReservations() override { return *peerReservations_; } @@ -739,25 +746,25 @@ public: } PendingSaves& - pendingSaves() override + getPendingSaves() override { return pendingSaves_; } OpenLedger& - openLedger() override + getOpenLedger() override { return *openLedger_; } OpenLedger const& - openLedger() const override + getOpenLedger() const override { return *openLedger_; } Overlay& - overlay() override + getOverlay() override { XRPL_ASSERT(overlay_, "xrpl::ApplicationImp::overlay : non-null overlay"); return *overlay_; @@ -775,8 +782,7 @@ public: { XRPL_ASSERT( relationalDatabase_, - "xrpl::ApplicationImp::getRelationalDatabase : non-null " - "relational database"); + "xrpl::ApplicationImp::getRelationalDatabase : non-null relational database"); return *relationalDatabase_; } @@ -791,7 +797,7 @@ public: serverOkay(std::string& reason) override; beast::Journal - journal(std::string const& name) override; + getJournal(std::string const& name) override; //-------------------------------------------------------------------------- @@ -823,7 +829,7 @@ public: } bool - initNodeStore() + initNodeStore() const { if (config_->doImport) { @@ -1067,7 +1073,7 @@ public: } virtual std::optional const& - trapTxID() const override + getTrapTxID() const override { return trapTxID_; } @@ -1103,7 +1109,7 @@ private: setMaxDisallowedLedger(); Application& - app() override + getApp() override { return *this; } @@ -1207,22 +1213,22 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) auto const startUp = config_->START_UP; JLOG(m_journal.debug()) << "startUp: " << startUp; - if (startUp == StartUpType::FRESH) + if (startUp == StartUpType::Fresh) { JLOG(m_journal.info()) << "Starting new Ledger"; startGenesisLedger(); } else if ( - startUp == StartUpType::LOAD || startUp == StartUpType::LOAD_FILE || - startUp == StartUpType::REPLAY) + startUp == StartUpType::Load || startUp == StartUpType::LoadFile || + startUp == StartUpType::Replay) { JLOG(m_journal.info()) << "Loading specified Ledger"; if (!loadOldLedger( config_->START_LEDGER, - startUp == StartUpType::REPLAY, - startUp == StartUpType::LOAD_FILE, + startUp == StartUpType::Replay, + startUp == StartUpType::LoadFile, config_->TRAP_TX_HASH)) { JLOG(m_journal.error()) << "The specified ledger could not be loaded."; @@ -1238,7 +1244,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) } } } - else if (startUp == StartUpType::NETWORK) + else if (startUp == StartUpType::Network) { // This should probably become the default once we have a stable // network. @@ -1346,7 +1352,8 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) { try { - auto setup = setup_ServerHandler(*config_, beast::logstream{m_journal.error()}); + beast::logstream logStream{m_journal.error()}; + auto setup = setup_ServerHandler(*config_, logStream); setup.makeContexts(); serverHandler_->setup(setup, m_journal); fixConfigPorts(*config_, serverHandler_->endpoints()); @@ -1392,7 +1399,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) "implications and have"; JLOG(m_journal.warn()) << "*** been deprecated. They will be removed " "in a future release of"; - JLOG(m_journal.warn()) << "*** rippled."; + JLOG(m_journal.warn()) << "*** xrpld."; JLOG(m_journal.warn()) << "*** If you do not use them to sign " "transactions please edit your"; JLOG(m_journal.warn()) << "*** configuration file and remove the [enable_signing] stanza."; @@ -1404,7 +1411,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) // // Execute start up rpc commands. // - for (auto cmd : config_->section(SECTION_RPC_STARTUP).lines()) + for (auto const& cmd : config_->section(SECTION_RPC_STARTUP).lines()) { Json::Reader jrReader; Json::Value jvCommand; @@ -1423,7 +1430,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) Resource::Charge loadType = Resource::feeReferenceRPC; Resource::Consumer c; RPC::JsonContext context{ - {journal("RPCHandler"), + {getJournal("RPCHandler"), *this, loadType, getOPs(), @@ -1539,11 +1546,11 @@ ApplicationImp::run() // TODO Store manifests in manifests.sqlite instead of wallet.db validatorManifests_->save(getWalletDB(), "ValidatorManifests", [this](PublicKey const& pubKey) { - return validators().listed(pubKey); + return getValidators().listed(pubKey); }); publisherManifests_->save(getWalletDB(), "PublisherManifests", [this](PublicKey const& pubKey) { - return validators().trustedPublisher(pubKey); + return getValidators().trustedPublisher(pubKey); }); // The order of these stop calls is delicate. @@ -1572,7 +1579,9 @@ ApplicationImp::signalStop(std::string msg) if (!isTimeToStop.test_and_set(std::memory_order_acquire)) { if (msg.empty()) + { JLOG(m_journal.warn()) << "Server stopping"; + } else JLOG(m_journal.warn()) << "Server stopping: " << msg; @@ -1626,15 +1635,19 @@ ApplicationImp::fdRequired() const void ApplicationImp::startGenesisLedger() { - std::vector const initialAmendments = (config_->START_UP == StartUpType::FRESH) + std::vector const initialAmendments = (config_->START_UP == StartUpType::Fresh) ? m_amendmentTable->getDesired() : std::vector{}; - std::shared_ptr const genesis = - std::make_shared(create_genesis, *config_, initialAmendments, nodeFamily_); + std::shared_ptr const genesis = std::make_shared( + create_genesis, + Rules{config_->features}, + config_->FEES.toFees(), + initialAmendments, + nodeFamily_); m_ledgerMaster->storeLedger(genesis); - auto const next = std::make_shared(*genesis, timeKeeper().closeTime()); + auto const next = std::make_shared(*genesis, getTimeKeeper().closeTime()); next->updateSkipList(); XRPL_ASSERT( next->header().seq < XRP_LEDGER_EARLIEST_FEES || next->read(keylet::fees()), @@ -1648,11 +1661,12 @@ ApplicationImp::startGenesisLedger() std::shared_ptr ApplicationImp::getLastFullLedger() { - auto j = journal("Ledger"); + auto j = getJournal("Ledger"); try { - auto const [ledger, seq, hash] = getLatestLedger(*this); + auto const [ledger, seq, hash] = + getLatestLedger(Rules{config_->features}, config_->FEES.toFees(), *this); if (!ledger) return ledger; @@ -1720,7 +1734,7 @@ ApplicationImp::loadLedgerFromFile(std::string const& name) ledger = ledger.get()["ledger"]; std::uint32_t seq = 1; - auto closeTime = timeKeeper().closeTime(); + auto closeTime = getTimeKeeper().closeTime(); using namespace std::chrono_literals; auto closeTimeResolution = 30s; bool closeTimeEstimated = false; @@ -1763,7 +1777,8 @@ ApplicationImp::loadLedgerFromFile(std::string const& name) return nullptr; } - auto loadLedger = std::make_shared(seq, closeTime, *config_, nodeFamily_); + auto loadLedger = std::make_shared( + seq, closeTime, Rules{config_->features}, config_->FEES.toFees(), nodeFamily_); loadLedger->setTotalDrops(totalDrops); for (Json::UInt index = 0; index < ledger.get().size(); ++index) @@ -1796,7 +1811,7 @@ ApplicationImp::loadLedgerFromFile(std::string const& name) // VFALCO TODO This is the only place that // constructor is used, try to remove it - STLedgerEntry sle(*stp.object, uIndex); + STLedgerEntry const sle(*stp.object, uIndex); if (!loadLedger->addSLE(sle)) { @@ -1843,7 +1858,8 @@ ApplicationImp::loadOldLedger( if (hash.parseHex(ledgerID)) { - loadLedger = loadByHash(hash, *this); + loadLedger = + loadByHash(hash, Rules{config_->features}, config_->FEES.toFees(), *this); if (!loadLedger) { @@ -1867,10 +1883,13 @@ ApplicationImp::loadOldLedger( else { // assume by sequence - std::uint32_t index; + std::uint32_t index = 0; if (beast::lexicalCastChecked(index, ledgerID)) - loadLedger = loadByIndex(index, *this); + { + loadLedger = + loadByIndex(index, Rules{config_->features}, config_->FEES.toFees(), *this); + } } if (!loadLedger) @@ -1885,7 +1904,11 @@ ApplicationImp::loadOldLedger( JLOG(m_journal.info()) << "Loading parent ledger"; - loadLedger = loadByHash(replayLedger->header().parentHash, *this); + loadLedger = loadByHash( + replayLedger->header().parentHash, + Rules{config_->features}, + config_->FEES.toFees(), + *this); if (!loadLedger) { JLOG(m_journal.info()) << "Loading parent ledger from node store"; @@ -1926,7 +1949,7 @@ ApplicationImp::loadOldLedger( << " UTC.\n" "This replay will not handle your ledger as it was " "originally " - "handled.\nConsider running an earlier version of rippled " + "handled.\nConsider running an earlier version of xrpld " "to " "get the older rules.\n*** CONTINUING ***\n"; } @@ -1943,7 +1966,7 @@ ApplicationImp::loadOldLedger( // LCOV_EXCL_STOP } - if (!loadLedger->walkLedger(journal("Ledger"), true)) + if (!loadLedger->walkLedger(getJournal("Ledger"), true)) { // LCOV_EXCL_START JLOG(m_journal.fatal()) << "Ledger is missing nodes."; @@ -1954,10 +1977,13 @@ ApplicationImp::loadOldLedger( // LCOV_EXCL_STOP } - if (!loadLedger->assertSensible(journal("Ledger"))) + if (!loadLedger->isSensible()) { // LCOV_EXCL_START - JLOG(m_journal.fatal()) << "Ledger is not sensible."; + Json::Value j = getJson({*loadLedger, {}}); + j[jss::accountTreeHash] = to_string(loadLedger->header().accountHash); + j[jss::transTreeHash] = to_string(loadLedger->header().txHash); + JLOG(m_journal.fatal()) << "Ledger is not sensible: " << j; UNREACHABLE( "xrpl::ApplicationImp::loadOldLedger : ledger is not " "sensible"); @@ -2072,7 +2098,7 @@ ApplicationImp::serverOkay(std::string& reason) } beast::Journal -ApplicationImp::journal(std::string const& name) +ApplicationImp::getJournal(std::string const& name) { return logs_->journal(name); } @@ -2118,7 +2144,7 @@ fixConfigPorts(Config& config, Endpoints const& endpoints) if (optPort) { std::uint16_t const port = beast::lexicalCast(*optPort); - if (!port) + if (port == 0u) section.set("port", std::to_string(ep.port())); } } diff --git a/src/xrpld/app/main/Application.h b/src/xrpld/app/main/Application.h index 0000ae010b..45bd94adce 100644 --- a/src/xrpld/app/main/Application.h +++ b/src/xrpld/app/main/Application.h @@ -18,16 +18,16 @@ namespace xrpl { namespace unl { class Manager; -} +} // namespace unl namespace Resource { class Manager; -} +} // namespace Resource namespace NodeStore { class Database; } // namespace NodeStore namespace perf { class PerfLog; -} +} // namespace perf // VFALCO TODO Fix forward declares required for header dependency loops class AmendmentTable; @@ -66,7 +66,7 @@ class NetworkOPs; class OpenLedger; class OrderBookDB; class Overlay; -class PathRequests; +class PathRequestManager; class PendingSaves; class PublicKey; class ServerHandler; diff --git a/src/xrpld/app/main/BasicApp.cpp b/src/xrpld/app/main/BasicApp.cpp index be0c158b85..2c5a2b0364 100644 --- a/src/xrpld/app/main/BasicApp.cpp +++ b/src/xrpld/app/main/BasicApp.cpp @@ -9,7 +9,7 @@ BasicApp::BasicApp(std::size_t numberOfThreads) work_.emplace(boost::asio::make_work_guard(io_context_)); threads_.reserve(numberOfThreads); - while (numberOfThreads--) + while ((numberOfThreads--) != 0u) { threads_.emplace_back([this, numberOfThreads]() { beast::setCurrentThreadName("io svc #" + std::to_string(numberOfThreads)); diff --git a/src/xrpld/app/main/GRPCServer.cpp b/src/xrpld/app/main/GRPCServer.cpp index c6b5c91e14..c8017d9ac0 100644 --- a/src/xrpld/app/main/GRPCServer.cpp +++ b/src/xrpld/app/main/GRPCServer.cpp @@ -16,8 +16,8 @@ getEndpoint(std::string const& peer) { try { - std::size_t first = peer.find_first_of(":"); - std::size_t last = peer.find_last_of(":"); + std::size_t const first = peer.find_first_of(':'); + std::size_t const last = peer.find_last_of(':'); std::string peerClean(peer); if (first != last) { @@ -56,7 +56,7 @@ GRPCServerImpl::CallData::CallData( , bindListener_(std::move(bindListener)) , handler_(std::move(handler)) , forward_(std::move(forward)) - , requiredCondition_(std::move(requiredCondition)) + , requiredCondition_(requiredCondition) , loadType_(std::move(loadType)) , secureGatewayIPs_(secureGatewayIPs) { @@ -88,7 +88,7 @@ GRPCServerImpl::CallData::process() // sanity check BOOST_ASSERT(!finished_); - std::shared_ptr> thisShared = this->shared_from_this(); + std::shared_ptr> const thisShared = this->shared_from_this(); // Need to set finished to true before processing the response, // because as soon as the response is posted to the completion @@ -107,7 +107,7 @@ GRPCServerImpl::CallData::process() // If coro is null, then the JobQueue has already been shutdown if (!coro) { - grpc::Status status{grpc::StatusCode::INTERNAL, "Job Queue is already stopped"}; + grpc::Status const status{grpc::StatusCode::INTERNAL, "Job Queue is already stopped"}; responder_.FinishWithError(status, this); } } @@ -119,10 +119,10 @@ GRPCServerImpl::CallData::process(std::shared_ptr::process(std::shared_ptr context{ - {app_.journal("gRPCServer"), + {app_.getJournal("gRPCServer"), app_, loadType, app_.getOPs(), @@ -162,12 +162,12 @@ GRPCServerImpl::CallData::process(std::shared_ptr::process(std::shared_ptr::getRole(bool isUnlimited) { if (isUnlimited) + { return Role::IDENTIFIED; - else - return Role::USER; + } + + return Role::USER; } template @@ -283,7 +285,8 @@ GRPCServerImpl::CallData::getUsage() Throw("Failed to get client endpoint"); } -GRPCServerImpl::GRPCServerImpl(Application& app) : app_(app), journal_(app_.journal("gRPC Server")) +GRPCServerImpl::GRPCServerImpl(Application& app) + : app_(app), journal_(app_.getJournal("gRPC Server")) { // if present, get endpoint from config if (app_.config().exists(SECTION_PORT_GRPC)) @@ -299,7 +302,7 @@ GRPCServerImpl::GRPCServerImpl(Application& app) : app_(app), journal_(app_.jour return; try { - boost::asio::ip::tcp::endpoint endpoint( + boost::asio::ip::tcp::endpoint const endpoint( boost::asio::ip::make_address(*optIp), std::stoi(*optPort)); std::stringstream ss; @@ -383,8 +386,8 @@ GRPCServerImpl::handleRpcs() requests.pop_back(); }; - void* tag; // uniquely identifies a request. - bool ok; + void* tag = nullptr; // uniquely identifies a request. + bool ok = false; // Block waiting to read the next event from the completion queue. The // event is uniquely identified by its tag, which in this case is the // memory address of a CallData instance. @@ -554,7 +557,7 @@ GRPCServer::start() { thread_ = std::thread([this]() { // Start the event loop and begin handling requests - beast::setCurrentThreadName("rippled: grpc"); + beast::setCurrentThreadName("xrpld: grpc"); this->impl_.handleRpcs(); }); } diff --git a/src/xrpld/app/main/GRPCServer.h b/src/xrpld/app/main/GRPCServer.h index 037c91df93..7fa9364174 100644 --- a/src/xrpld/app/main/GRPCServer.h +++ b/src/xrpld/app/main/GRPCServer.h @@ -234,14 +234,14 @@ private: getClientEndpoint(); // If the request was proxied through - // another rippled node, returns the ip of the originating client. + // another xrpld node, returns the ip of the originating client. // Empty optional if request was not proxied or there was an error // decoding the client ip std::optional getProxiedClientIpAddress(); // If the request was proxied through - // another rippled node, returns the endpoint of the originating client. + // another xrpld node, returns the endpoint of the originating client. // Empty optional if request was not proxied or there was an error // decoding the client endpoint std::optional @@ -261,7 +261,7 @@ private: bool clientIsUnlimited(); - // True if the request was proxied through another rippled node prior + // True if the request was proxied through another xrpld node prior // to arriving here bool wasForwarded(); diff --git a/src/xrpld/app/main/LoadManager.cpp b/src/xrpld/app/main/LoadManager.cpp index 5d2bd9a81e..47948a4031 100644 --- a/src/xrpld/app/main/LoadManager.cpp +++ b/src/xrpld/app/main/LoadManager.cpp @@ -13,7 +13,7 @@ namespace xrpl { LoadManager::LoadManager(Application& app, beast::Journal journal) - : app_(app), journal_(journal), lastHeartbeat_(), armed_(false) + : app_(app), journal_(journal), armed_(false) { } @@ -35,7 +35,7 @@ LoadManager::~LoadManager() void LoadManager::activateStallDetector() { - std::lock_guard sl(mutex_); + std::lock_guard const sl(mutex_); armed_ = true; lastHeartbeat_ = std::chrono::steady_clock::now(); } @@ -44,7 +44,7 @@ void LoadManager::heartbeat() { auto const heartbeat = std::chrono::steady_clock::now(); - std::lock_guard sl(mutex_); + std::lock_guard const sl(mutex_); lastHeartbeat_ = heartbeat; } @@ -63,7 +63,7 @@ void LoadManager::stop() { { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); stop_ = true; // There is at most one thread waiting on this condition. cv_.notify_all(); diff --git a/src/xrpld/app/main/Main.cpp b/src/xrpld/app/main/Main.cpp index 32d246f493..ebd5920492 100644 --- a/src/xrpld/app/main/Main.cpp +++ b/src/xrpld/app/main/Main.cpp @@ -61,7 +61,7 @@ adjustDescriptorLimit(int needed, beast::Journal j) { #ifdef RLIMIT_NOFILE // Get the current limit, then adjust it to what we need. - struct rlimit rl; + struct rlimit rl{}; int available = 0; @@ -69,9 +69,13 @@ adjustDescriptorLimit(int needed, beast::Journal j) { // If the limit is infinite, then we are good. if (rl.rlim_cur == RLIM_INFINITY) + { available = needed; + } else + { available = rl.rlim_cur; + } if (available < needed) { @@ -103,7 +107,7 @@ adjustDescriptorLimit(int needed, beast::Journal j) void printHelp(po::options_description const& desc) { - std::cerr << systemName() << "d [options] \n" + std::cerr << systemName() << " [options] \n" << desc << std::endl << "Commands: \n" " account_currencies []\n" @@ -195,8 +199,10 @@ public: operator()(beast::unit_test::suite_info const& s) { for (auto& sel : selectors_) + { if (sel(s)) return true; + } return false; } @@ -209,7 +215,7 @@ public: namespace test { extern std::atomic envUseIPv4; -} +} // namespace test template static bool @@ -251,11 +257,11 @@ runUnitTests( if (!child && num_jobs == 1) { - multi_runner_parent parent_runner; + multi_runner_parent const parent_runner; multi_runner_child child_runner{num_jobs, quiet, log}; child_runner.arg(argument); - multi_selector pred(pattern); + multi_selector const pred(pattern); auto const any_failed = child_runner.run_multi(pred) || anyMissing(child_runner, pred); if (any_failed) @@ -276,9 +282,12 @@ runUnitTests( args.emplace_back("--unittest-child"); } + children.reserve(num_jobs); for (std::size_t i = 0; i < num_jobs; ++i) + { children.emplace_back( boost::process::v1::exe = exe_name, boost::process::v1::args = args); + } int bad_child_exits = 0; int terminated_child_exits = 0; @@ -287,7 +296,7 @@ runUnitTests( try { c.wait(); - if (c.exit_code()) + if (c.exit_code() != 0) ++bad_child_exits; } catch (...) @@ -301,21 +310,19 @@ runUnitTests( parent_runner.add_failures(terminated_child_exits); anyMissing(parent_runner, multi_selector(pattern)); - if (parent_runner.any_failed() || bad_child_exits) + if (parent_runner.any_failed() || (bad_child_exits != 0)) return EXIT_FAILURE; return EXIT_SUCCESS; } - else - { - // child - multi_runner_child runner{num_jobs, quiet, log}; - runner.arg(argument); - auto const anyFailed = runner.run_multi(multi_selector(pattern)); - if (anyFailed) - return EXIT_FAILURE; - return EXIT_SUCCESS; - } + // child + multi_runner_child runner{num_jobs, quiet, log}; + runner.arg(argument); + auto const anyFailed = runner.run_multi(multi_selector(pattern)); + + if (anyFailed) + return EXIT_FAILURE; + return EXIT_SUCCESS; } #endif // ENABLE_TESTS @@ -463,20 +470,20 @@ run(int argc, char** argv) } catch (std::exception const& ex) { - std::cerr << "rippled: " << ex.what() << std::endl; - std::cerr << "Try 'rippled --help' for a list of options." << std::endl; + std::cerr << "xrpld: " << ex.what() << std::endl; + std::cerr << "Try 'xrpld --help' for a list of options." << std::endl; return 1; } - if (vm.count("help")) + if (vm.contains("help")) { printHelp(desc); return 0; } - if (vm.count("version")) + if (vm.contains("version")) { - std::cout << "rippled version " << BuildInfo::getVersionString() << std::endl; + std::cout << "xrpld version " << BuildInfo::getVersionString() << std::endl; std::cout << "Git commit hash: " << xrpl::git::getCommitHash() << std::endl; std::cout << "Git build branch: " << xrpl::git::getBuildBranch() << std::endl; return 0; @@ -485,65 +492,61 @@ run(int argc, char** argv) #ifndef ENABLE_TESTS if (vm.count("unittest") || vm.count("unittest-child")) { - std::cerr << "rippled: Tests disabled in this build." << std::endl; - std::cerr << "Try 'rippled --help' for a list of options." << std::endl; + std::cerr << "xrpld: Tests disabled in this build." << std::endl; + std::cerr << "Try 'xrpld --help' for a list of options." << std::endl; return 1; } #else // Run the unit tests if requested. // The unit tests will exit the application with an appropriate return code. // - if (vm.count("unittest")) + if (vm.contains("unittest")) { std::string argument; - if (vm.count("unittest-arg")) + if (vm.contains("unittest-arg")) argument = vm["unittest-arg"].as(); std::size_t numJobs = 1; bool unittestChild = false; - if (vm.count("unittest-jobs")) + if (vm.contains("unittest-jobs")) numJobs = std::max(numJobs, vm["unittest-jobs"].as()); - unittestChild = bool(vm.count("unittest-child")); + unittestChild = vm.contains("unittest-child"); return runUnitTests( vm["unittest"].as(), argument, - bool(vm.count("quiet")), - bool(vm.count("unittest-log")), + vm.contains("quiet"), + vm.contains("unittest-log"), unittestChild, - bool(vm.count("unittest-ipv6")), + vm.contains("unittest-ipv6"), numJobs, argc, argv); } // LCOV_EXCL_START - else + + if (vm.contains("unittest-jobs")) { - if (vm.count("unittest-jobs")) - { - // unittest jobs only makes sense with `unittest` - std::cerr << "rippled: '--unittest-jobs' specified without " - "'--unittest'.\n"; - std::cerr << "To run the unit tests the '--unittest' option must " - "be present.\n"; - return 1; - } + // unittest jobs only makes sense with `unittest` + std::cerr << "xrpld: '--unittest-jobs' specified without " + "'--unittest'.\n"; + std::cerr << "To run the unit tests the '--unittest' option must " + "be present.\n"; + return 1; } + #endif // ENABLE_TESTS auto config = std::make_unique(); - auto configFile = vm.count("conf") ? vm["conf"].as() : std::string(); + auto configFile = vm.contains("conf") ? vm["conf"].as() : std::string(); // config file, quiet flag. config->setup( - configFile, - bool(vm.count("quiet")), - bool(vm.count("silent")), - bool(vm.count("standalone"))); + configFile, vm.contains("quiet"), vm.contains("silent"), vm.contains("standalone")); - if (vm.count("vacuum")) + if (vm.contains("vacuum")) { if (config->standalone()) { @@ -609,21 +612,21 @@ run(int argc, char** argv) } } - if (vm.count("start")) + if (vm.contains("start")) { - config->START_UP = StartUpType::FRESH; + config->START_UP = StartUpType::Fresh; } - if (vm.count("import")) + if (vm.contains("import")) config->doImport = true; - if (vm.count("ledger")) + if (vm.contains("ledger")) { config->START_LEDGER = vm["ledger"].as(); - if (vm.count("replay")) + if (vm.contains("replay")) { - config->START_UP = StartUpType::REPLAY; - if (vm.count("trap_tx_hash")) + config->START_UP = StartUpType::Replay; + if (vm.contains("trap_tx_hash")) { uint256 tmp = {}; auto hash = vm["trap_tx_hash"].as(); @@ -641,43 +644,45 @@ run(int argc, char** argv) } } else - config->START_UP = StartUpType::LOAD; + { + config->START_UP = StartUpType::Load; + } } - else if (vm.count("ledgerfile")) + else if (vm.contains("ledgerfile")) { config->START_LEDGER = vm["ledgerfile"].as(); - config->START_UP = StartUpType::LOAD_FILE; + config->START_UP = StartUpType::LoadFile; } - else if (vm.count("load") || config->FAST_LOAD) + else if (vm.contains("load") || config->FAST_LOAD) { - config->START_UP = StartUpType::LOAD; + config->START_UP = StartUpType::Load; } - if (vm.count("trap_tx_hash") && vm.count("replay") == 0) + if (vm.contains("trap_tx_hash") && !vm.contains("replay")) { std::cerr << "Cannot use trap option without replay option" << std::endl; return -1; } - if (vm.count("net") && !config->FAST_LOAD) + if (vm.contains("net") && !config->FAST_LOAD) { - if ((config->START_UP == StartUpType::LOAD) || (config->START_UP == StartUpType::REPLAY)) + if ((config->START_UP == StartUpType::Load) || (config->START_UP == StartUpType::Replay)) { std::cerr << "Net and load/replay options are incompatible" << std::endl; return -1; } - config->START_UP = StartUpType::NETWORK; + config->START_UP = StartUpType::Network; } - if (vm.count("valid")) + if (vm.contains("valid")) { config->START_VALID = true; } // Override the RPC destination IP address. This must // happen after the config file is loaded. - if (vm.count("rpc_ip")) + if (vm.contains("rpc_ip")) { auto endpoint = beast::IP::Endpoint::from_string_checked(vm["rpc_ip"].as()); if (!endpoint) @@ -689,7 +694,7 @@ run(int argc, char** argv) if (endpoint->port() == 0) { std::cerr << "No port specified in rpc_ip.\n"; - if (vm.count("rpc_port")) + if (vm.contains("rpc_port")) { std::cerr << "WARNING: using deprecated rpc_port param.\n"; try @@ -705,13 +710,15 @@ run(int argc, char** argv) } } else + { return -1; + } } config->rpc_ip = std::move(*endpoint); } - if (vm.count("quorum")) + if (vm.contains("quorum")) { try { @@ -732,18 +739,20 @@ run(int argc, char** argv) using namespace beast::severities; Severity thresh = kInfo; - if (vm.count("quiet")) + if (vm.contains("quiet")) + { thresh = kFatal; - else if (vm.count("verbose")) + } + else if (vm.contains("verbose")) + { thresh = kTrace; + } auto logs = std::make_unique(thresh); // No arguments. Run server. - if (!vm.count("parameters")) + if (!vm.contains("parameters")) { - // TODO: this comment can be removed in a future release - - // say 1.7 or higher if (config->had_trailing_comments()) { JLOG(logs->journal("Application").warn()) @@ -760,7 +769,7 @@ run(int argc, char** argv) if (!adjustDescriptorLimit(1024, logs->journal("Application"))) return -1; - if (vm.count("debug")) + if (vm.contains("debug")) setDebugLogSink(logs->makeSink("Debug", beast::severities::kTrace)); auto app = @@ -771,7 +780,7 @@ run(int argc, char** argv) // With our configuration parsed, ensure we have // enough file descriptors available: - if (!adjustDescriptorLimit(app->fdRequired(), app->logs().journal("Application"))) + if (!adjustDescriptorLimit(app->fdRequired(), app->getJournal("Application"))) return -1; // Start the server @@ -784,7 +793,7 @@ run(int argc, char** argv) } // We have an RPC command to process: - beast::setCurrentThreadName("rippled: rpc"); + beast::setCurrentThreadName("xrpld: rpc"); return RPCCall::fromCommandLine( *config, vm["parameters"].as>(), *logs); // LCOV_EXCL_STOP diff --git a/src/xrpld/app/main/NodeIdentity.cpp b/src/xrpld/app/main/NodeIdentity.cpp index 3019caeb31..bc2c943b10 100644 --- a/src/xrpld/app/main/NodeIdentity.cpp +++ b/src/xrpld/app/main/NodeIdentity.cpp @@ -12,7 +12,7 @@ getNodeIdentity(Application& app, boost::program_options::variables_map const& c { std::optional seed; - if (cmdline.count("nodeid")) + if (cmdline.contains("nodeid")) { seed = parseGenericSeed(cmdline["nodeid"].as(), false); @@ -37,7 +37,7 @@ getNodeIdentity(Application& app, boost::program_options::variables_map const& c auto db = app.getWalletDB().checkoutDb(); - if (cmdline.count("newnodeid") != 0) + if (cmdline.contains("newnodeid")) clearNodeIdentity(*db); return getNodeIdentity(*db); diff --git a/src/xrpld/app/misc/DeliverMax.h b/src/xrpld/app/misc/DeliverMax.h index 1b4241f091..7fec517d28 100644 --- a/src/xrpld/app/misc/DeliverMax.h +++ b/src/xrpld/app/misc/DeliverMax.h @@ -4,7 +4,7 @@ namespace Json { class Value; -} +} // namespace Json namespace xrpl { diff --git a/src/xrpld/app/misc/FeeEscalation.md b/src/xrpld/app/misc/FeeEscalation.md index 7843620320..5a3edbe95a 100644 --- a/src/xrpld/app/misc/FeeEscalation.md +++ b/src/xrpld/app/misc/FeeEscalation.md @@ -1,6 +1,6 @@ # Fees -Rippled's fee mechanism consists of several interrelated processes: +Xrpld's fee mechanism consists of several interrelated processes: 1. [Rapid Fee escalation](#fee-escalation) 2. [The Transaction Queue](#transaction-queue) @@ -184,7 +184,7 @@ than a more complex transaction paying more XRP. ### Load Fee -Each rippled server maintains a minimum cost threshold based on its current load. If you submit a transaction with a fee that is lower than the current load-based transaction cost of the rippled server, the server neither applies nor relays the transaction to its peers. A transaction is very unlikely to survive the consensus process unless its transaction fee value meets the requirements of a majority of servers. +Each xrpld server maintains a minimum cost threshold based on its current load. If you submit a transaction with a fee that is lower than the current load-based transaction cost of the xrpld server, the server neither applies nor relays the transaction to its peers. A transaction is very unlikely to survive the consensus process unless its transaction fee value meets the requirements of a majority of servers. ### Reference Transaction @@ -193,7 +193,7 @@ single-signed transaction (eg. Payment, Account Set, Offer Create, etc) that requires a fee. In the future, there may be other transaction types that require -more (or less) work for rippled to process. Those transactions may have +more (or less) work for xrpld to process. Those transactions may have a higher (or lower) base fee, requiring a correspondingly higher (or lower) fee to get into the same position as a reference transaction. @@ -211,7 +211,7 @@ Another factor to consider is the duration of the consensus process itself. This generally takes under 5 seconds on the main network under low volume. This is based on historical observations. However factors such as transaction volume -can increase consensus duration. This is because rippled performs +can increase consensus duration. This is because xrpld performs more work as transaction volume increases. Under sufficient load this tends to increase consensus duration. It's possible that relatively high consensus duration indicates a problem, but it is not appropriate to @@ -293,7 +293,7 @@ values by 5 for a multi-signed transaction with 4 signatures.) The `fee` result is always instantaneous, and relates to the open ledger. It includes the sequence number of the current open ledger, -but may not make sense if rippled is not synced to the network. +but may not make sense if xrpld is not synced to the network. Result format: diff --git a/src/xrpld/app/misc/FeeVoteImpl.cpp b/src/xrpld/app/misc/FeeVoteImpl.cpp index 5cb203115a..414d8d7421 100644 --- a/src/xrpld/app/misc/FeeVoteImpl.cpp +++ b/src/xrpld/app/misc/FeeVoteImpl.cpp @@ -1,7 +1,8 @@ -#include #include +#include #include +#include #include #include @@ -177,9 +178,13 @@ FeeVoteImpl::doVoting( { auto const vote = field->xrp(); if (isLegalAmountSigned(vote)) + { value.addVote(vote); + } else + { value.noVote(); + } } else { @@ -207,12 +212,16 @@ FeeVoteImpl::doVoting( auto const vote = *field; if (vote <= std::numeric_limits::max() && isLegalAmountSigned(XRPAmount{unsafe_cast(vote)})) + { value.addVote(XRPAmount{unsafe_cast(vote)}); + } else + { // Invalid amounts will be treated as if they're // not provided. Don't throw because this value is // provided by an external entity. value.noVote(); + } } else { @@ -246,7 +255,7 @@ FeeVoteImpl::doVoting( JLOG(journal_.warn()) << "We are voting for a fee change: " << baseFee.first << "/" << baseReserve.first << "/" << incReserve.first; - STTx feeTx(ttFEE, [=, &rules](auto& obj) { + STTx const feeTx(ttFEE, [=, &rules](auto& obj) { obj[sfAccount] = AccountID(); obj[sfLedgerSequence] = seq; if (rules.enabled(featureXRPFees)) @@ -264,11 +273,11 @@ FeeVoteImpl::doVoting( baseReserve.first.dropsAs(baseReserveVote.current()); obj[sfReserveIncrement] = incReserve.first.dropsAs(incReserveVote.current()); - obj[sfReferenceFeeUnits] = Config::FEE_UNITS_DEPRECATED; + obj[sfReferenceFeeUnits] = FEE_UNITS_DEPRECATED; } }); - uint256 txID = feeTx.getTransactionID(); + uint256 const txID = feeTx.getTransactionID(); JLOG(journal_.warn()) << "Vote: " << txID; diff --git a/src/xrpld/app/misc/NegativeUNLVote.cpp b/src/xrpld/app/misc/NegativeUNLVote.cpp index 6673785b61..212cfaa2e9 100644 --- a/src/xrpld/app/misc/NegativeUNLVote.cpp +++ b/src/xrpld/app/misc/NegativeUNLVote.cpp @@ -1,7 +1,7 @@ #include -#include #include +#include #include namespace xrpl { @@ -52,7 +52,7 @@ NegativeUNLVote::doVoting( { auto nid = calcNodeID(k); negUnlNodeIDs.emplace(nid); - if (!nidToKeyMap.count(nid)) + if (!nidToKeyMap.contains(nid)) { nidToKeyMap.emplace(nid, k); } @@ -90,7 +90,7 @@ NegativeUNLVote::addTx( NegativeUNLModify modify, std::shared_ptr const& initialSet) { - STTx negUnlTx(ttUNL_MODIFY, [&](auto& obj) { + STTx const negUnlTx(ttUNL_MODIFY, [&](auto& obj) { obj.setFieldU8(sfUNLModifyDisabling, modify == ToDisable ? 1 : 0); obj.setFieldU32(sfLedgerSequence, seq); obj.setFieldVL(sfUNLModifyValidator, vp.slice()); @@ -118,7 +118,7 @@ NegativeUNLVote::choose(uint256 const& randomPadData, std::vector const& { XRPL_ASSERT(!candidates.empty(), "xrpl::NegativeUNLVote::choose : non-empty input"); static_assert(NodeID::bytes <= uint256::bytes); - NodeID randomPad = NodeID::fromVoid(randomPadData.data()); + NodeID const randomPad = NodeID::fromVoid(randomPadData.data()); NodeID txNodeID = candidates[0]; for (int j = 1; j < candidates.size(); ++j) { @@ -175,7 +175,7 @@ NegativeUNLVote::buildScoreTable( for (auto const& v : validations.getTrustedForLedger(ledgerAncestors[numAncestors - 1 - i], seq - 2 - i)) { - if (scoreTable.count(v->getNodeID())) + if (scoreTable.contains(v->getNodeID())) ++scoreTable[v->getNodeID()]; } } @@ -195,24 +195,20 @@ NegativeUNLVote::buildScoreTable( << " The reliability measurement could be wrong."; return {}; } - else if ( - myValidationCount > negativeUNLMinLocalValsToVote && + if (myValidationCount > negativeUNLMinLocalValsToVote && myValidationCount <= FLAG_LEDGER_INTERVAL) { return scoreTable; } - else - { - // cannot happen because validations.getTrustedForLedger does not - // return multiple validations of the same ledger from a validator. - JLOG(j_.error()) << "N-UNL: ledger " << seq << ". Local node issued " << myValidationCount - << " validations in last " << FLAG_LEDGER_INTERVAL - << " ledgers. Too many!"; - return {}; - } + + // cannot happen because validations.getTrustedForLedger does not + // return multiple validations of the same ledger from a validator. + JLOG(j_.error()) << "N-UNL: ledger " << seq << ". Local node issued " << myValidationCount + << " validations in last " << FLAG_LEDGER_INTERVAL << " ledgers. Too many!"; + return {}; } -NegativeUNLVote::Candidates const +NegativeUNLVote::Candidates NegativeUNLVote::findAllCandidates( hash_set const& unl, hash_set const& negUnl, @@ -225,7 +221,7 @@ NegativeUNLVote::findAllCandidates( std::size_t negativeListed = 0; for (auto const& n : unl) { - if (negUnl.count(n)) + if (negUnl.contains(n)) ++negativeListed; } bool const result = negativeListed < maxNegativeListed; @@ -246,8 +242,8 @@ NegativeUNLVote::findAllCandidates( // (2) has less than negativeUNLLowWaterMark validations, // (3) is not in negUnl, and // (4) is not a new validator. - if (canAdd && score < negativeUNLLowWaterMark && !negUnl.count(nodeId) && - !newValidators_.count(nodeId)) + if (canAdd && score < negativeUNLLowWaterMark && !negUnl.contains(nodeId) && + !newValidators_.contains(nodeId)) { JLOG(j_.trace()) << "N-UNL: toDisable candidate " << nodeId; candidates.toDisableCandidates.push_back(nodeId); @@ -256,7 +252,7 @@ NegativeUNLVote::findAllCandidates( // Find toReEnable Candidates: check if // (1) has more than negativeUNLHighWaterMark validations, // (2) is in negUnl - if (score > negativeUNLHighWaterMark && negUnl.count(nodeId)) + if (score > negativeUNLHighWaterMark && negUnl.contains(nodeId)) { JLOG(j_.trace()) << "N-UNL: toReEnable candidate " << nodeId; candidates.toReEnableCandidates.push_back(nodeId); @@ -277,7 +273,7 @@ NegativeUNLVote::findAllCandidates( { for (auto const& n : negUnl) { - if (!unl.count(n)) + if (!unl.contains(n)) { candidates.toReEnableCandidates.push_back(n); } @@ -289,10 +285,10 @@ NegativeUNLVote::findAllCandidates( void NegativeUNLVote::newValidators(LedgerIndex seq, hash_set const& nowTrusted) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); for (auto const& n : nowTrusted) { - if (newValidators_.find(n) == newValidators_.end()) + if (!newValidators_.contains(n)) { JLOG(j_.trace()) << "N-UNL: add a new validator " << n << " at ledger seq=" << seq; newValidators_[n] = seq; @@ -303,7 +299,7 @@ NegativeUNLVote::newValidators(LedgerIndex seq, hash_set const& nowTrust void NegativeUNLVote::purgeNewValidators(LedgerIndex seq) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto i = newValidators_.begin(); while (i != newValidators_.end()) { diff --git a/src/xrpld/app/misc/NegativeUNLVote.h b/src/xrpld/app/misc/NegativeUNLVote.h index 95646af034..2cc63cf8c5 100644 --- a/src/xrpld/app/misc/NegativeUNLVote.h +++ b/src/xrpld/app/misc/NegativeUNLVote.h @@ -1,8 +1,7 @@ #pragma once -#include - #include +#include #include #include #include @@ -140,7 +139,7 @@ private: * @param candidates the vector of candidates * @return the picked candidate */ - NodeID + static NodeID choose(uint256 const& randomPadData, std::vector const& candidates); /** @@ -172,7 +171,7 @@ private: * @param scoreTable the score table * @return the candidates to disable and the candidates to re-enable */ - Candidates const + Candidates findAllCandidates( hash_set const& unl, hash_set const& negUnl, diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index b8663a76fb..361eada455 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -43,11 +43,15 @@ #include #include #include +#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -165,15 +169,15 @@ class NetworkOPsImp final : public NetworkOPs struct CounterData { decltype(counters_) counters; - decltype(mode_) mode; + decltype(mode_) mode = OperatingMode::DISCONNECTED; decltype(start_) start; - decltype(initialSyncUs_) initialSyncUs; + decltype(initialSyncUs_) initialSyncUs{}; }; CounterData getCounterData() const { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return {counters_, mode_, start_, initialSyncUs_}; } }; @@ -185,7 +189,7 @@ class NetworkOPsImp final : public NetworkOPs ServerFeeSummary( XRPAmount fee, - TxQ::Metrics&& escalationMetrics, + TxQ::Metrics escalationMetrics, // trivially copyable LoadFeeTrack const& loadFeeTrack); bool operator!=(ServerFeeSummary const& b) const; @@ -212,27 +216,27 @@ public: JobQueue& job_queue, LedgerMaster& ledgerMaster, ValidatorKeys const& validatorKeys, - boost::asio::io_context& io_svc, + boost::asio::io_context& ioCtx, beast::Journal journal, beast::insight::Collector::ptr const& collector) : registry_(registry) , m_journal(journal) , m_localTX(make_LocalTxs()) , mMode(start_valid ? OperatingMode::FULL : OperatingMode::DISCONNECTED) - , heartbeatTimer_(io_svc) - , clusterTimer_(io_svc) - , accountHistoryTxTimer_(io_svc) + , heartbeatTimer_(ioCtx) + , clusterTimer_(ioCtx) + , accountHistoryTxTimer_(ioCtx) , mConsensus( - registry_.app(), + registry_.get().getApp(), make_FeeVote( - setup_FeeVote(registry_.app().config().section("voting")), - registry_.logs().journal("FeeVote")), + setup_FeeVote(registry_.get().getApp().config().section("voting")), + registry_.get().getJournal("FeeVote")), ledgerMaster, *m_localTX, registry.getInboundTransactions(), beast::get_abstract_clock(), validatorKeys, - registry_.logs().journal("LedgerConsensus")) + registry_.get().getJournal("LedgerConsensus")) , validatorPK_( validatorKeys.keys ? validatorKeys.keys->publicKey : decltype(validatorPK_){}) , validatorMasterPK_( @@ -648,23 +652,16 @@ private: { AccountID const accountId_; // forward - std::uint32_t forwardTxIndex_; + std::uint32_t forwardTxIndex_{0}; // separate backward and forward - std::uint32_t separationLedgerSeq_; + std::uint32_t separationLedgerSeq_{0}; // history, backward - std::uint32_t historyLastLedgerSeq_; - std::int32_t historyTxIndex_; - bool haveHistorical_; - std::atomic stopHistorical_; + std::uint32_t historyLastLedgerSeq_{0}; + std::int32_t historyTxIndex_{-1}; + bool haveHistorical_{false}; + std::atomic stopHistorical_{false}; - SubAccountHistoryIndex(AccountID const& accountId) - : accountId_(accountId) - , forwardTxIndex_(0) - , separationLedgerSeq_(0) - , historyLastLedgerSeq_(0) - , historyTxIndex_(-1) - , haveHistorical_(false) - , stopHistorical_(false) + SubAccountHistoryIndex(AccountID const& accountId) : accountId_(accountId) { } }; @@ -693,7 +690,7 @@ private: void setAccountHistoryJobTimer(SubAccountHistoryInfoWeak subInfo); - ServiceRegistry& registry_; + std::reference_wrapper registry_; beast::Journal m_journal; std::unique_ptr m_localTX; @@ -717,7 +714,7 @@ private: std::optional const validatorPK_; std::optional const validatorMasterPK_; - ConsensusPhase mLastConsensusPhase; + ConsensusPhase mLastConsensusPhase{ConsensusPhase::open}; LedgerMaster& m_ledgerMaster; @@ -759,7 +756,7 @@ private: DispatchState mDispatchState = DispatchState::none; std::vector mTransactions; - StateAccounting accounting_{}; + StateAccounting accounting_; std::set pendingValidations_; std::mutex validationsMutex_; @@ -880,7 +877,7 @@ NetworkOPsImp::getHostId(bool forAdmin) // For non-admin uses hash the node public key into a // single RFC1751 word: static std::string const shroudedHostId = [this]() { - auto const& id = registry_.app().nodeIdentity(); + auto const& id = registry_.get().getApp().nodeIdentity(); return RFC1751::getWordFromBlob(id.first.data(), id.first.size()); }(); @@ -894,7 +891,7 @@ NetworkOPsImp::setStateTimer() setHeartbeatTimer(); // Only do this work if a cluster is configured - if (registry_.cluster().size() != 0) + if (registry_.get().getCluster().size() != 0) setClusterTimer(); } @@ -972,13 +969,13 @@ NetworkOPsImp::processHeartbeatTimer() { RclConsensusLogger clog("Heartbeat Timer", mConsensus.validating(), m_journal); { - std::unique_lock lock{registry_.app().getMasterMutex()}; + std::unique_lock lock{registry_.get().getApp().getMasterMutex()}; // VFALCO NOTE This is for diagnosing a crash on exit - LoadManager& mgr(registry_.getLoadManager()); + LoadManager& mgr(registry_.get().getLoadManager()); mgr.heartbeat(); - std::size_t const numPeers = registry_.overlay().size(); + std::size_t const numPeers = registry_.get().getOverlay().size(); // do we have sufficient peers? If not, we are disconnected. if (numPeers < minPeerCount_) @@ -1019,9 +1016,13 @@ NetworkOPsImp::processHeartbeatTimer() auto origMode = mMode.load(); CLOG(clog.ss()) << "mode: " << strOperatingMode(origMode, true); if (mMode == OperatingMode::SYNCING) + { setMode(OperatingMode::SYNCING); + } else if (mMode == OperatingMode::CONNECTED) + { setMode(OperatingMode::CONNECTED); + } auto newMode = mMode.load(); if (origMode != newMode) { @@ -1030,7 +1031,7 @@ NetworkOPsImp::processHeartbeatTimer() CLOG(clog.ss()) << ". "; } - mConsensus.timerEntry(registry_.timeKeeper().closeTime(), clog.ss()); + mConsensus.timerEntry(registry_.get().getTimeKeeper().closeTime(), clog.ss()); CLOG(clog.ss()) << "consensus phase " << to_string(mLastConsensusPhase); ConsensusPhase const currPhase = mConsensus.phase(); @@ -1048,17 +1049,18 @@ NetworkOPsImp::processHeartbeatTimer() void NetworkOPsImp::processClusterTimer() { - if (registry_.cluster().size() == 0) + if (registry_.get().getCluster().size() == 0) return; using namespace std::chrono_literals; - bool const update = registry_.cluster().update( - registry_.app().nodeIdentity().first, + bool const update = registry_.get().getCluster().update( + registry_.get().getApp().nodeIdentity().first, "", - (m_ledgerMaster.getValidatedLedgerAge() <= 4min) ? registry_.getFeeTrack().getLocalFee() - : 0, - registry_.timeKeeper().now()); + (m_ledgerMaster.getValidatedLedgerAge() <= 4min) + ? registry_.get().getFeeTrack().getLocalFee() + : 0, + registry_.get().getTimeKeeper().now()); if (!update) { @@ -1068,7 +1070,7 @@ NetworkOPsImp::processClusterTimer() } protocol::TMCluster cluster; - registry_.cluster().for_each([&cluster](ClusterNode const& node) { + registry_.get().getCluster().for_each([&cluster](ClusterNode const& node) { protocol::TMClusterNode& n = *cluster.add_clusternodes(); n.set_publickey(toBase58(TokenType::NodePublic, node.identity())); n.set_reporttime(node.getReportTime().time_since_epoch().count()); @@ -1077,14 +1079,14 @@ NetworkOPsImp::processClusterTimer() n.set_nodename(node.name()); }); - Resource::Gossip gossip = registry_.getResourceManager().exportConsumers(); + Resource::Gossip const gossip = registry_.get().getResourceManager().exportConsumers(); for (auto& item : gossip.items) { protocol::TMLoadSource& node = *cluster.add_loadsources(); node.set_name(to_string(item.address)); node.set_cost(item.balance); } - registry_.overlay().foreach( + registry_.get().getOverlay().foreach( send_if(std::make_shared(cluster, protocol::mtCLUSTER), peer_in_cluster())); setClusterTimer(); } @@ -1130,7 +1132,7 @@ NetworkOPsImp::submitTransaction(std::shared_ptr const& iTrans) auto const trans = sterilize(*iTrans); auto const txid = trans->getTransactionID(); - auto const flags = registry_.getHashRouter().getFlags(txid); + auto const flags = registry_.get().getHashRouter().getFlags(txid); if ((flags & HashRouterFlags::BAD) != HashRouterFlags::UNDEFINED) { @@ -1140,8 +1142,8 @@ NetworkOPsImp::submitTransaction(std::shared_ptr const& iTrans) try { - auto const [validity, reason] = - checkValidity(registry_.getHashRouter(), *trans, m_ledgerMaster.getValidatedRules()); + auto const [validity, reason] = checkValidity( + registry_.get().getHashRouter(), *trans, m_ledgerMaster.getValidatedRules()); if (validity != Validity::Valid) { @@ -1158,7 +1160,7 @@ NetworkOPsImp::submitTransaction(std::shared_ptr const& iTrans) std::string reason; - auto tx = std::make_shared(trans, reason, registry_.app()); + auto tx = std::make_shared(trans, reason, registry_.get().getApp()); m_job_queue.addJob(jtTRANSACTION, "SubmitTxn", [this, tx]() { auto t = tx; @@ -1169,7 +1171,7 @@ NetworkOPsImp::submitTransaction(std::shared_ptr const& iTrans) bool NetworkOPsImp::preProcessTransaction(std::shared_ptr& transaction) { - auto const newFlags = registry_.getHashRouter().getFlags(transaction->getID()); + auto const newFlags = registry_.get().getHashRouter().getFlags(transaction->getID()); if ((newFlags & HashRouterFlags::BAD) != HashRouterFlags::UNDEFINED) { @@ -1190,14 +1192,15 @@ NetworkOPsImp::preProcessTransaction(std::shared_ptr& transaction) { transaction->setStatus(INVALID); transaction->setResult(temINVALID_FLAG); - registry_.getHashRouter().setFlags(transaction->getID(), HashRouterFlags::BAD); + registry_.get().getHashRouter().setFlags(transaction->getID(), HashRouterFlags::BAD); return false; } // NOTE ximinez - I think this check is redundant, // but I'm not 100% sure yet. // If so, only cost is looking up HashRouter flags. - auto const [validity, reason] = checkValidity(registry_.getHashRouter(), sttx, view->rules()); + auto const [validity, reason] = + checkValidity(registry_.get().getHashRouter(), sttx, view->rules()); XRPL_ASSERT( validity == Validity::Valid, "xrpl::NetworkOPsImp::processTransaction : valid validity"); @@ -1207,12 +1210,12 @@ NetworkOPsImp::preProcessTransaction(std::shared_ptr& transaction) JLOG(m_journal.info()) << "Transaction has bad signature: " << reason; transaction->setStatus(INVALID); transaction->setResult(temBAD_SIGNATURE); - registry_.getHashRouter().setFlags(transaction->getID(), HashRouterFlags::BAD); + registry_.get().getHashRouter().setFlags(transaction->getID(), HashRouterFlags::BAD); return false; } // canonicalize can change our pointer - registry_.getMasterTransaction().canonicalize(&transaction); + registry_.get().getMasterTransaction().canonicalize(&transaction); return true; } @@ -1231,9 +1234,13 @@ NetworkOPsImp::processTransaction( return; if (bLocal) + { doTransactionSync(transaction, bUnlimited, failType); + } else + { doTransactionAsync(transaction, bUnlimited, failType); + } } void @@ -1242,7 +1249,7 @@ NetworkOPsImp::doTransactionAsync( bool bUnlimited, FailHard failType) { - std::lock_guard lock(mMutex); + std::lock_guard const lock(mMutex); if (transaction->getApplying()) return; @@ -1294,7 +1301,7 @@ NetworkOPsImp::doTransactionSyncBatch( { apply(lock); - if (mTransactions.size()) + if (!mTransactions.empty()) { // More transactions need to be applied, but by another job. if (m_job_queue.addJob(jtBATCH, "TxBatchSync", [this]() { transactionBatch(); })) @@ -1315,7 +1322,7 @@ NetworkOPsImp::processTransactionSet(CanonicalTXSet const& set) for (auto const& [_, tx] : set) { std::string reason; - auto transaction = std::make_shared(tx, reason, registry_.app()); + auto transaction = std::make_shared(tx, reason, registry_.get().getApp()); if (transaction->getStatus() == INVALID) { @@ -1323,7 +1330,7 @@ NetworkOPsImp::processTransactionSet(CanonicalTXSet const& set) { JLOG(m_journal.trace()) << "Exception checking transaction: " << reason; } - registry_.getHashRouter().setFlags(tx->getTransactionID(), HashRouterFlags::BAD); + registry_.get().getHashRouter().setFlags(tx->getTransactionID(), HashRouterFlags::BAD); continue; } @@ -1349,7 +1356,9 @@ NetworkOPsImp::processTransactionSet(CanonicalTXSet const& set) } if (mTransactions.empty()) + { mTransactions.swap(transactions); + } else { mTransactions.reserve(mTransactions.size() + transactions.size()); @@ -1378,7 +1387,7 @@ NetworkOPsImp::transactionBatch() if (mDispatchState == DispatchState::running) return; - while (mTransactions.size()) + while (!mTransactions.empty()) { apply(lock); } @@ -1399,13 +1408,12 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) batchLock.unlock(); { - std::unique_lock masterLock{registry_.app().getMasterMutex(), std::defer_lock}; + std::unique_lock masterLock{registry_.get().getApp().getMasterMutex(), std::defer_lock}; bool changed = false; { std::unique_lock ledgerLock{m_ledgerMaster.peekMutex(), std::defer_lock}; std::lock(masterLock, ledgerLock); - - registry_.openLedger().modify([&](OpenView& view, beast::Journal j) { + registry_.get().getOpenLedger().modify([&](OpenView& view, beast::Journal j) { for (TransactionStatus& e : transactions) { // we check before adding to the batch @@ -1416,8 +1424,8 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) if (e.failType == FailHard::yes) flags |= tapFAIL_HARD; - auto const result = registry_.getTxQ().apply( - registry_.app(), view, e.transaction->getSTransaction(), flags, j); + auto const result = registry_.get().getTxQ().apply( + registry_.get().getApp(), view, e.transaction->getSTransaction(), flags, j); e.result = result.ter; e.applied = result.applied; changed = changed || result.applied; @@ -1432,8 +1440,8 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) if (auto const l = m_ledgerMaster.getValidatedLedger()) validatedLedgerIndex = l->header().seq; - auto newOL = registry_.openLedger().current(); - for (TransactionStatus& e : transactions) + auto newOL = registry_.get().getOpenLedger().current(); + for (TransactionStatus const& e : transactions) { e.transaction->clearSubmitResult(); @@ -1446,10 +1454,13 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) e.transaction->setResult(e.result); if (isTemMalformed(e.result)) - registry_.getHashRouter().setFlags(e.transaction->getID(), HashRouterFlags::BAD); + { + registry_.get().getHashRouter().setFlags( + e.transaction->getID(), HashRouterFlags::BAD); + } #ifdef DEBUG - if (e.result != tesSUCCESS) + if (!isTesSuccess(e.result)) { std::string token, human; @@ -1460,9 +1471,9 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) } #endif - bool addLocal = e.local; + bool const addLocal = e.local; - if (e.result == tesSUCCESS) + if (isTesSuccess(e.result)) { JLOG(m_journal.debug()) << "Transaction is now included in open ledger"; e.transaction->setStatus(INCLUDED); @@ -1481,7 +1492,7 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) batchLock.lock(); std::string reason; auto const trans = sterilize(*txNext); - auto t = std::make_shared(trans, reason, registry_.app()); + auto t = std::make_shared(trans, reason, registry_.get().getApp()); if (t->getApplying()) break; submit_held.emplace_back(t, false, false, FailHard::no); @@ -1535,7 +1546,7 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) // up!) // if (e.local || (ledgersLeft && ledgersLeft <= LocalTxs::holdLedgers) || - registry_.getHashRouter().setFlags( + registry_.get().getHashRouter().setFlags( e.transaction->getID(), HashRouterFlags::HELD)) { // transaction should be held @@ -1572,7 +1583,8 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) (e.result == terQUEUED)) && !enforceFailHard) { - auto const toSkip = registry_.getHashRouter().shouldRelay(e.transaction->getID()); + auto const toSkip = + registry_.get().getHashRouter().shouldRelay(e.transaction->getID()); if (auto const sttx = *(e.transaction->getSTransaction()); toSkip && // Skip relaying if it's an inner batch txn. The flag should // only be set if the Batch feature is enabled. If Batch is @@ -1587,18 +1599,19 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) tx.set_rawtransaction(s.data(), s.size()); tx.set_status(protocol::tsCURRENT); tx.set_receivetimestamp( - registry_.timeKeeper().now().time_since_epoch().count()); + registry_.get().getTimeKeeper().now().time_since_epoch().count()); tx.set_deferred(e.result == terQUEUED); // FIXME: This should be when we received it - registry_.overlay().relay(e.transaction->getID(), tx, *toSkip); + registry_.get().getOverlay().relay(e.transaction->getID(), tx, *toSkip); e.transaction->setBroadcast(); } } if (validatedLedgerIndex) { - auto [fee, accountSeq, availableSeq] = registry_.getTxQ().getTxRequiredFeeAndSeq( - *newOL, e.transaction->getSTransaction()); + auto [fee, accountSeq, availableSeq] = + registry_.get().getTxQ().getTxRequiredFeeAndSeq( + *newOL, e.transaction->getSTransaction()); e.transaction->setCurrentLedgerState( *validatedLedgerIndex, fee, accountSeq, availableSeq); } @@ -1607,13 +1620,15 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) batchLock.lock(); - for (TransactionStatus& e : transactions) + for (TransactionStatus const& e : transactions) e.transaction->clearApplying(); if (!submit_held.empty()) { if (mTransactions.empty()) + { mTransactions.swap(submit_held); + } else { mTransactions.reserve(mTransactions.size() + submit_held.size()); @@ -1639,7 +1654,7 @@ NetworkOPsImp::getOwnerInfo(std::shared_ptr lpLedger, AccountID auto sleNode = lpLedger->read(keylet::page(root)); if (sleNode) { - std::uint64_t uNodeDir; + std::uint64_t uNodeDir = 0; do { @@ -1680,12 +1695,12 @@ NetworkOPsImp::getOwnerInfo(std::shared_ptr lpLedger, AccountID uNodeDir = sleNode->getFieldU64(sfIndexNext); - if (uNodeDir) + if (uNodeDir != 0u) { sleNode = lpLedger->read(keylet::page(root, uNodeDir)); XRPL_ASSERT(sleNode, "xrpl::NetworkOPsImp::getOwnerInfo : read next page"); } - } while (uNodeDir); + } while (uNodeDir != 0u); } return jvObjects; @@ -1767,14 +1782,14 @@ NetworkOPsImp::checkLastClosedLedger(Overlay::PeerSequence const& peerList, uint return false; uint256 closedLedger = ourClosed->header().hash; - uint256 prevClosedLedger = ourClosed->header().parentHash; + uint256 const prevClosedLedger = ourClosed->header().parentHash; JLOG(m_journal.trace()) << "OurClosed: " << closedLedger; JLOG(m_journal.trace()) << "PrevClosed: " << prevClosedLedger; //------------------------------------------------------------------------- // Determine preferred last closed ledger - auto& validations = registry_.getValidations(); + auto& validations = registry_.get().getValidations(); JLOG(m_journal.debug()) << "ValidationTrie " << Json::Compact(validations.getJsonTrie()); // Will rely on peer LCL if no trusted validations exist @@ -1785,7 +1800,7 @@ NetworkOPsImp::checkLastClosedLedger(Overlay::PeerSequence const& peerList, uint for (auto& peer : peerList) { - uint256 peerLedger = peer->getClosedLedgerHash(); + uint256 const peerLedger = peer->getClosedLedgerHash(); if (peerLedger.isNonZero()) ++peerCounts[peerLedger]; @@ -1794,7 +1809,7 @@ NetworkOPsImp::checkLastClosedLedger(Overlay::PeerSequence const& peerList, uint for (auto const& it : peerCounts) JLOG(m_journal.debug()) << "L: " << it.first << " n=" << it.second; - uint256 preferredLCL = validations.getPreferredLCL( + uint256 const preferredLCL = validations.getPreferredLCL( RCLValidatedLedger{ourClosed, validations.adaptor().journal()}, m_ledgerMaster.getValidLedgerIndex(), peerCounts); @@ -1811,7 +1826,9 @@ NetworkOPsImp::checkLastClosedLedger(Overlay::PeerSequence const& peerList, uint switchLedgers = false; } else + { networkClosed = closedLedger; + } if (!switchLedgers) return false; @@ -1819,8 +1836,10 @@ NetworkOPsImp::checkLastClosedLedger(Overlay::PeerSequence const& peerList, uint auto consensus = m_ledgerMaster.getLedgerByHash(closedLedger); if (!consensus) - consensus = registry_.getInboundLedgers().acquire( + { + consensus = registry_.get().getInboundLedgers().acquire( closedLedger, 0, InboundLedger::Reason::CONSENSUS); + } if (consensus && (!m_ledgerMaster.canBeCurrent(consensus) || @@ -1861,7 +1880,7 @@ NetworkOPsImp::switchLastClosedLedger(std::shared_ptr const& newLC clearNeedNetworkLedger(); // Update fee computations. - registry_.getTxQ().processClosedLedger(registry_.app(), *newLCL, true); + registry_.get().getTxQ().processClosedLedger(registry_.get().getApp(), *newLCL, true); // Caller must own master lock { @@ -1869,14 +1888,18 @@ NetworkOPsImp::switchLastClosedLedger(std::shared_ptr const& newLC // open ledger. Then apply local tx. auto retries = m_localTX->getTxSet(); - auto const lastVal = registry_.getLedgerMaster().getValidatedLedger(); + auto const lastVal = registry_.get().getLedgerMaster().getValidatedLedger(); std::optional rules; if (lastVal) - rules = makeRulesGivenLedger(*lastVal, registry_.app().config().features); + { + rules = makeRulesGivenLedger(*lastVal, registry_.get().getApp().config().features); + } else - rules.emplace(registry_.app().config().features); - registry_.openLedger().accept( - registry_.app(), + { + rules.emplace(registry_.get().getApp().config().features); + } + registry_.get().getOpenLedger().accept( + registry_.get().getApp(), *rules, newLCL, OrderedTxs({}), @@ -1886,7 +1909,7 @@ NetworkOPsImp::switchLastClosedLedger(std::shared_ptr const& newLC "jump", [&](OpenView& view, beast::Journal j) { // Stuff the ledger with transactions from the queue. - return registry_.getTxQ().accept(registry_.app(), view); + return registry_.get().getTxQ().accept(registry_.get().getApp(), view); }); } @@ -1895,12 +1918,11 @@ NetworkOPsImp::switchLastClosedLedger(std::shared_ptr const& newLC protocol::TMStatusChange s; s.set_newevent(protocol::neSWITCHED_LEDGER); s.set_ledgerseq(newLCL->header().seq); - s.set_networktime(registry_.timeKeeper().now().time_since_epoch().count()); + s.set_networktime(registry_.get().getTimeKeeper().now().time_since_epoch().count()); s.set_ledgerhashprevious( newLCL->header().parentHash.begin(), newLCL->header().parentHash.size()); s.set_ledgerhash(newLCL->header().hash.begin(), newLCL->header().hash.size()); - - registry_.overlay().foreach( + registry_.get().getOverlay().foreach( send_always(std::make_shared(s, protocol::mtSTATUS_CHANGE))); } @@ -1941,23 +1963,24 @@ NetworkOPsImp::beginConsensus( "xrpl::NetworkOPsImp::beginConsensus : closedLedger parent matches " "hash"); - registry_.validators().setNegativeUNL(prevLedger->negativeUNL()); - TrustChanges const changes = registry_.validators().updateTrusted( - registry_.getValidations().getCurrentNodeIDs(), + registry_.get().getValidators().setNegativeUNL(prevLedger->negativeUNL()); + TrustChanges const changes = registry_.get().getValidators().updateTrusted( + registry_.get().getValidations().getCurrentNodeIDs(), closingInfo.parentCloseTime, *this, - registry_.overlay(), - registry_.getHashRouter()); + registry_.get().getOverlay(), + registry_.get().getHashRouter()); if (!changes.added.empty() || !changes.removed.empty()) { - registry_.getValidations().trustChanged(changes.added, changes.removed); + registry_.get().getValidations().trustChanged(changes.added, changes.removed); // Update the AmendmentTable so it tracks the current validators. - registry_.getAmendmentTable().trustChanged(registry_.validators().getQuorumKeys().second); + registry_.get().getAmendmentTable().trustChanged( + registry_.get().getValidators().getQuorumKeys().second); } mConsensus.startRound( - registry_.timeKeeper().closeTime(), + registry_.get().getTimeKeeper().closeTime(), networkClosed, prevLedger, changes.removed, @@ -1997,33 +2020,30 @@ NetworkOPsImp::processTrustedProposal(RCLCxPeerPos peerPos) return false; } - return mConsensus.peerProposal(registry_.timeKeeper().closeTime(), peerPos); + return mConsensus.peerProposal(registry_.get().getTimeKeeper().closeTime(), peerPos); } void NetworkOPsImp::mapComplete(std::shared_ptr const& map, bool fromAcquire) { // We now have an additional transaction set - // either created locally during the consensus process - // or acquired from a peer - // Inform peers we have this set protocol::TMHaveTransactionSet msg; msg.set_hash(map->getHash().as_uint256().begin(), 256 / 8); msg.set_status(protocol::tsHAVE); - registry_.overlay().foreach(send_always(std::make_shared(msg, protocol::mtHAVE_SET))); + registry_.get().getOverlay().foreach( + send_always(std::make_shared(msg, protocol::mtHAVE_SET))); // We acquired it because consensus asked us to if (fromAcquire) - mConsensus.gotTxSet(registry_.timeKeeper().closeTime(), RCLTxSet{map}); + mConsensus.gotTxSet(registry_.get().getTimeKeeper().closeTime(), RCLTxSet{map}); } void NetworkOPsImp::endConsensus(std::unique_ptr const& clog) { - uint256 deadLedger = m_ledgerMaster.getClosedLedger()->header().parentHash; - - for (auto const& it : registry_.overlay().getActivePeers()) + uint256 const deadLedger = m_ledgerMaster.getClosedLedger()->header().parentHash; + for (auto const& it : registry_.get().getOverlay().getActivePeers()) { if (it && (it->getClosedLedgerHash() == deadLedger)) { @@ -2033,7 +2053,8 @@ NetworkOPsImp::endConsensus(std::unique_ptr const& clog) } uint256 networkClosed; - bool ledgerChange = checkLastClosedLedger(registry_.overlay().getActivePeers(), networkClosed); + bool const ledgerChange = + checkLastClosedLedger(registry_.get().getOverlay().getActivePeers(), networkClosed); if (networkClosed.isZero()) { @@ -2063,7 +2084,7 @@ NetworkOPsImp::endConsensus(std::unique_ptr const& clog) // Note: Do not go to FULL if we don't have the previous ledger // check if the ledger is bad enough to go to CONNECTED -- TODO auto current = m_ledgerMaster.getCurrentLedger(); - if (registry_.timeKeeper().now() < + if (registry_.get().getTimeKeeper().now() < (current->header().parentCloseTime + 2 * current->header().closeTimeResolution)) { setMode(OperatingMode::FULL); @@ -2086,7 +2107,7 @@ void NetworkOPsImp::pubManifest(Manifest const& mo) { // VFALCO consider std::shared_mutex - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); if (!mStreamMaps[sManifests].empty()) { @@ -2121,12 +2142,12 @@ NetworkOPsImp::pubManifest(Manifest const& mo) NetworkOPsImp::ServerFeeSummary::ServerFeeSummary( XRPAmount fee, - TxQ::Metrics&& escalationMetrics, + TxQ::Metrics escalationMetrics, // trivially copyable LoadFeeTrack const& loadFeeTrack) : loadFactorServer{loadFeeTrack.getLoadFactor()} , loadBaseServer{loadFeeTrack.getLoadBase()} , baseFee{fee} - , em{std::move(escalationMetrics)} + , em{escalationMetrics} { } @@ -2164,16 +2185,16 @@ NetworkOPsImp::pubServer() // list into a local array while holding the lock then release // the lock and call send on everyone. // - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); if (!mStreamMaps[sServer].empty()) { Json::Value jvObj(Json::objectValue); ServerFeeSummary f{ - registry_.openLedger().current()->fees().base, - registry_.getTxQ().getMetrics(*registry_.openLedger().current()), - registry_.getFeeTrack()}; + registry_.get().getOpenLedger().current()->fees().base, + registry_.get().getTxQ().getMetrics(*registry_.get().getOpenLedger().current()), + registry_.get().getFeeTrack()}; jvObj[jss::type] = "serverStatus"; jvObj[jss::server_status] = strOperatingMode(); @@ -2194,13 +2215,15 @@ NetworkOPsImp::pubServer() jvObj[jss::load_factor_fee_reference] = f.em->referenceFeeLevel.jsonClipped(); } else + { jvObj[jss::load_factor] = f.loadFactorServer; + } mLastFeeSummary = f; for (auto i = mStreamMaps[sServer].begin(); i != mStreamMaps[sServer].end();) { - InfoSub::pointer p = i->second.lock(); + InfoSub::pointer const p = i->second.lock(); // VFALCO TODO research the possibility of using thread queues and // linearizing the deletion of subscribers with the @@ -2221,7 +2244,7 @@ NetworkOPsImp::pubServer() void NetworkOPsImp::pubConsensus(ConsensusPhase phase) { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); auto& streamMap = mStreamMaps[sConsensusPhase]; if (!streamMap.empty()) @@ -2249,7 +2272,7 @@ void NetworkOPsImp::pubValidation(std::shared_ptr const& val) { // VFALCO consider std::shared_mutex - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); if (!mStreamMaps[sValidations].empty()) { @@ -2265,7 +2288,7 @@ NetworkOPsImp::pubValidation(std::shared_ptr const& val) jvObj[jss::flags] = val->getFlags(); jvObj[jss::signing_time] = *(*val)[~sfSigningTime]; jvObj[jss::data] = strHex(val->getSerializer().slice()); - jvObj[jss::network_id] = registry_.getNetworkIDService().getNetworkID(); + jvObj[jss::network_id] = registry_.get().getNetworkIDService().getNetworkID(); if (auto version = (*val)[~sfServerVersion]) jvObj[jss::server_version] = std::to_string(*version); @@ -2276,7 +2299,7 @@ NetworkOPsImp::pubValidation(std::shared_ptr const& val) if (auto hash = (*val)[~sfValidatedHash]) jvObj[jss::validated_hash] = strHex(*hash); - auto const masterKey = registry_.validatorManifests().getMasterKey(signerPublic); + auto const masterKey = registry_.get().getValidatorManifests().getMasterKey(signerPublic); if (masterKey != signerPublic) jvObj[jss::master_key] = toBase58(TokenType::NodePublic, masterKey); @@ -2354,7 +2377,7 @@ NetworkOPsImp::pubValidation(std::shared_ptr const& val) void NetworkOPsImp::pubPeerStatus(std::function const& func) { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); if (!mStreamMaps[sPeerStatus].empty()) { @@ -2364,7 +2387,7 @@ NetworkOPsImp::pubPeerStatus(std::function const& func) for (auto i = mStreamMaps[sPeerStatus].begin(); i != mStreamMaps[sPeerStatus].end();) { - InfoSub::pointer p = i->second.lock(); + InfoSub::pointer const p = i->second.lock(); if (p) { @@ -2385,12 +2408,12 @@ NetworkOPsImp::setMode(OperatingMode om) using namespace std::chrono_literals; if (om == OperatingMode::CONNECTED) { - if (registry_.getLedgerMaster().getValidatedLedgerAge() < 1min) + if (registry_.get().getLedgerMaster().getValidatedLedgerAge() < 1min) om = OperatingMode::SYNCING; } else if (om == OperatingMode::SYNCING) { - if (registry_.getLedgerMaster().getValidatedLedgerAge() >= 1min) + if (registry_.get().getLedgerMaster().getValidatedLedgerAge() >= 1min) om = OperatingMode::CONNECTED; } @@ -2418,11 +2441,15 @@ NetworkOPsImp::recvValidation(std::shared_ptr const& val, std::str try { if (pendingValidations_.contains(val->getLedgerHash())) + { bypassAccept = BypassAccept::yes; + } else + { pendingValidations_.insert(val->getLedgerHash()); - scope_unlock unlock(lock); - handleNewValidation(registry_.app(), val, source, bypassAccept, m_journal); + } + scope_unlock const unlock(lock); + handleNewValidation(registry_.get().getApp(), val, source, bypassAccept, m_journal); } catch (std::exception const& e) { @@ -2445,7 +2472,7 @@ NetworkOPsImp::recvValidation(std::shared_ptr const& val, std::str JLOG(m_journal.debug()) << [this, &val]() -> auto { std::stringstream ss; ss << "VALIDATION: " << val->render() << " master_key: "; - auto master = registry_.validators().getTrustedKey(val->getSignerPublic()); + auto master = registry_.get().getValidators().getTrustedKey(val->getSignerPublic()); if (master) { ss << toBase58(TokenType::NodePublic, *master); @@ -2459,7 +2486,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_.app().config().RELAY_UNTRUSTED_VALIDATIONS == 1 || val->isTrusted(); + return registry_.get().getApp().config().RELAY_UNTRUSTED_VALIDATIONS == 1 || val->isTrusted(); } Json::Value @@ -2501,7 +2528,8 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) "One or more unsupported amendments have reached majority. " "Upgrade to the latest version before they are activated " "to avoid being amendment blocked."; - if (auto const expected = registry_.getAmendmentTable().firstUnsupportedExpected()) + if (auto const expected = + registry_.get().getAmendmentTable().firstUnsupportedExpected()) { auto& d = w[jss::details] = Json::objectValue; d[jss::expected_date] = expected->time_since_epoch().count(); @@ -2509,7 +2537,7 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) } } - if (warnings.size()) + if (warnings.size() != 0u) info[jss::warnings] = std::move(warnings); } @@ -2518,8 +2546,8 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) info[jss::hostid] = getHostId(admin); // domain: if configured with a domain, report it: - if (!registry_.app().config().SERVER_DOMAIN.empty()) - info[jss::server_domain] = registry_.app().config().SERVER_DOMAIN; + if (!registry_.get().getApp().config().SERVER_DOMAIN.empty()) + info[jss::server_domain] = registry_.get().getApp().config().SERVER_DOMAIN; info[jss::build_version] = BuildInfo::getVersionString(); @@ -2531,11 +2559,15 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) if (needNetworkLedger_) info[jss::network_ledger] = "waiting"; - info[jss::validation_quorum] = static_cast(registry_.validators().quorum()); + info[jss::validation_quorum] = + static_cast(registry_.get().getValidators().quorum()); if (admin) { - switch (registry_.app().config().NODE_SIZE) + // 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) { case 0: info[jss::node_size] = "tiny"; @@ -2554,21 +2586,25 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) break; } - auto when = registry_.validators().expires(); + auto when = registry_.get().getValidators().expires(); if (!human) { if (when) + { info[jss::validator_list_expires] = safe_cast(when->time_since_epoch().count()); + } else + { info[jss::validator_list_expires] = 0; + } } else { auto& x = (info[jss::validator_list] = Json::objectValue); - x[jss::count] = static_cast(registry_.validators().count()); + x[jss::count] = static_cast(registry_.get().getValidators().count()); if (when) { @@ -2581,10 +2617,14 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) { x[jss::expiration] = to_string(*when); - if (*when > registry_.timeKeeper().now()) + if (*when > registry_.get().getTimeKeeper().now()) + { x[jss::status] = "active"; + } else + { x[jss::status] = "expired"; + } } } else @@ -2603,12 +2643,13 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) x[jss::branch] = xrpl::git::getBuildBranch(); } } - info[jss::io_latency_ms] = static_cast(registry_.app().getIOLatency().count()); + info[jss::io_latency_ms] = + static_cast(registry_.get().getApp().getIOLatency().count()); if (admin) { - if (auto const localPubKey = registry_.validators().localPublicKey(); - localPubKey && registry_.app().getValidationPublicKey()) + if (auto const localPubKey = registry_.get().getValidators().localPublicKey(); + localPubKey && registry_.get().getApp().getValidationPublicKey()) { info[jss::pubkey_validator] = toBase58(TokenType::NodePublic, localPubKey.value()); } @@ -2620,17 +2661,18 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) if (counters) { - info[jss::counters] = registry_.getPerfLog().countersJson(); + info[jss::counters] = registry_.get().getPerfLog().countersJson(); Json::Value nodestore(Json::objectValue); - registry_.getNodeStore().getCountsJson(nodestore); + registry_.get().getNodeStore().getCountsJson(nodestore); info[jss::counters][jss::nodestore] = nodestore; - info[jss::current_activities] = registry_.getPerfLog().currentJson(); + info[jss::current_activities] = registry_.get().getPerfLog().currentJson(); } - info[jss::pubkey_node] = toBase58(TokenType::NodePublic, registry_.app().nodeIdentity().first); + info[jss::pubkey_node] = + toBase58(TokenType::NodePublic, registry_.get().getApp().nodeIdentity().first); - info[jss::complete_ledgers] = registry_.getLedgerMaster().getCompleteLedgers(); + info[jss::complete_ledgers] = registry_.get().getLedgerMaster().getCompleteLedgers(); if (amendmentBlocked_) info[jss::amendment_blocked] = true; @@ -2640,7 +2682,7 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) if (fp != 0) info[jss::fetch_pack] = Json::UInt(fp); - info[jss::peers] = Json::UInt(registry_.overlay().size()); + info[jss::peers] = Json::UInt(registry_.get().getOverlay().size()); Json::Value lastClose = Json::objectValue; lastClose[jss::proposers] = Json::UInt(mConsensus.prevProposers()); @@ -2662,13 +2704,14 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) if (admin) info[jss::load] = m_job_queue.getJson(); - if (auto const netid = registry_.overlay().networkID()) + if (auto const netid = registry_.get().getOverlay().networkID()) info[jss::network_id] = static_cast(*netid); - auto const escalationMetrics = registry_.getTxQ().getMetrics(*registry_.openLedger().current()); + auto const escalationMetrics = + registry_.get().getTxQ().getMetrics(*registry_.get().getOpenLedger().current()); - auto const loadFactorServer = registry_.getFeeTrack().getLoadFactor(); - auto const loadBaseServer = registry_.getFeeTrack().getLoadBase(); + auto const loadFactorServer = registry_.get().getFeeTrack().getLoadFactor(); + auto const loadBaseServer = registry_.get().getFeeTrack().getLoadBase(); /* Scale the escalated fee level to unitless "load factor". In practice, this just strips the units, but it will continue to work correctly if either base value ever changes. */ @@ -2705,34 +2748,42 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) if (admin) { - std::uint32_t fee = registry_.getFeeTrack().getLocalFee(); + std::uint32_t fee = registry_.get().getFeeTrack().getLocalFee(); if (fee != loadBaseServer) info[jss::load_factor_local] = static_cast(fee) / loadBaseServer; - fee = registry_.getFeeTrack().getRemoteFee(); + fee = registry_.get().getFeeTrack().getRemoteFee(); if (fee != loadBaseServer) info[jss::load_factor_net] = static_cast(fee) / loadBaseServer; - fee = registry_.getFeeTrack().getClusterFee(); + fee = registry_.get().getFeeTrack().getClusterFee(); if (fee != loadBaseServer) info[jss::load_factor_cluster] = static_cast(fee) / loadBaseServer; } if (escalationMetrics.openLedgerFeeLevel != escalationMetrics.referenceFeeLevel && (admin || loadFactorFeeEscalation != loadFactor)) + { info[jss::load_factor_fee_escalation] = escalationMetrics.openLedgerFeeLevel.decimalFromReference( escalationMetrics.referenceFeeLevel); + } if (escalationMetrics.minProcessingFeeLevel != escalationMetrics.referenceFeeLevel) + { info[jss::load_factor_fee_queue] = escalationMetrics.minProcessingFeeLevel.decimalFromReference( escalationMetrics.referenceFeeLevel); + } } bool valid = false; auto lpClosed = m_ledgerMaster.getValidatedLedger(); if (lpClosed) + { valid = true; + } else + { lpClosed = m_ledgerMaster.getClosedLedger(); + } if (lpClosed) { @@ -2755,7 +2806,7 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) l[jss::reserve_base_xrp] = lpClosed->fees().reserve.decimalXRP(); l[jss::reserve_inc_xrp] = lpClosed->fees().increment.decimalXRP(); - if (auto const closeOffset = registry_.timeKeeper().closeOffset(); + if (auto const closeOffset = registry_.get().getTimeKeeper().closeOffset(); std::abs(closeOffset.count()) >= 60) l[jss::close_time_offset] = static_cast(closeOffset.count()); @@ -2768,7 +2819,7 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) else { auto lCloseTime = lpClosed->header().closeTime; - auto closeTime = registry_.timeKeeper().closeTime(); + auto closeTime = registry_.get().getTimeKeeper().closeTime(); if (lCloseTime <= closeTime) { using namespace std::chrono_literals; @@ -2779,23 +2830,32 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) } if (valid) + { info[jss::validated_ledger] = l; + } else + { info[jss::closed_ledger] = l; + } auto lpPublished = m_ledgerMaster.getPublishedLedger(); if (!lpPublished) + { info[jss::published_ledger] = "none"; + } else if (lpPublished->header().seq != lpClosed->header().seq) + { info[jss::published_ledger] = lpPublished->header().seq; + } } accounting_.json(info); info[jss::uptime] = UptimeClock::now().time_since_epoch().count(); - info[jss::jq_trans_overflow] = std::to_string(registry_.overlay().getJqTransOverflow()); - info[jss::peer_disconnects] = std::to_string(registry_.overlay().getPeerDisconnect()); + info[jss::jq_trans_overflow] = + std::to_string(registry_.get().getOverlay().getJqTransOverflow()); + info[jss::peer_disconnects] = std::to_string(registry_.get().getOverlay().getPeerDisconnect()); info[jss::peer_disconnects_resources] = - std::to_string(registry_.overlay().getPeerDisconnectCharges()); + std::to_string(registry_.get().getOverlay().getPeerDisconnectCharges()); // This array must be sorted in increasing order. static constexpr std::array protocols{ @@ -2803,7 +2863,7 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) static_assert(std::is_sorted(std::begin(protocols), std::end(protocols))); { Json::Value ports{Json::arrayValue}; - for (auto const& port : registry_.getServerHandler().setup().ports) + for (auto const& port : registry_.get().getServerHandler().setup().ports) { // Don't publish admin ports for non-admin users if (!admin && @@ -2827,9 +2887,9 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) } } - if (registry_.app().config().exists(SECTION_PORT_GRPC)) + if (registry_.get().getApp().config().exists(SECTION_PORT_GRPC)) { - auto const& grpcSection = registry_.app().config().section(SECTION_PORT_GRPC); + auto const& grpcSection = registry_.get().getApp().config().section(SECTION_PORT_GRPC); auto const optPort = grpcSection.get("port"); if (optPort && grpcSection.get("ip")) { @@ -2848,13 +2908,13 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) void NetworkOPsImp::clearLedgerFetch() { - registry_.getInboundLedgers().clearFailures(); + registry_.get().getInboundLedgers().clearFailures(); } Json::Value NetworkOPsImp::getLedgerFetchInfo() { - return registry_.getInboundLedgers().getInfo(); + return registry_.get().getInboundLedgers().getInfo(); } void @@ -2873,7 +2933,7 @@ NetworkOPsImp::pubProposedTransaction( MultiApiJson jvObj = transJson(transaction, result, false, ledger, std::nullopt); { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); auto it = mStreamMaps[sRTTransactions].begin(); while (it != mStreamMaps[sRTTransactions].end()) @@ -2904,11 +2964,11 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) // Holes are filled across connection loss or other catastrophe std::shared_ptr alpAccepted = - registry_.getAcceptedLedgerCache().fetch(lpAccepted->header().hash); + registry_.get().getAcceptedLedgerCache().fetch(lpAccepted->header().hash); if (!alpAccepted) { alpAccepted = std::make_shared(lpAccepted); - registry_.getAcceptedLedgerCache().canonicalize_replace_client( + registry_.get().getAcceptedLedgerCache().canonicalize_replace_client( lpAccepted->header().hash, alpAccepted); } @@ -2920,7 +2980,7 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) JLOG(m_journal.debug()) << "Publishing ledger " << lpAccepted->header().seq << " " << lpAccepted->header().hash; - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); if (!mStreamMaps[sLedger].empty()) { @@ -2932,10 +2992,10 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) jvObj[jss::ledger_time] = Json::Value::UInt(lpAccepted->header().closeTime.time_since_epoch().count()); - jvObj[jss::network_id] = registry_.getNetworkIDService().getNetworkID(); + jvObj[jss::network_id] = registry_.get().getNetworkIDService().getNetworkID(); if (!lpAccepted->rules().enabled(featureXRPFees)) - jvObj[jss::fee_ref] = Config::FEE_UNITS_DEPRECATED; + jvObj[jss::fee_ref] = FEE_UNITS_DEPRECATED; jvObj[jss::fee_base] = lpAccepted->fees().base.jsonClipped(); jvObj[jss::reserve_base] = lpAccepted->fees().reserve.jsonClipped(); jvObj[jss::reserve_inc] = lpAccepted->fees().increment.jsonClipped(); @@ -2944,38 +3004,43 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) if (mMode >= OperatingMode::SYNCING) { - jvObj[jss::validated_ledgers] = registry_.getLedgerMaster().getCompleteLedgers(); + jvObj[jss::validated_ledgers] = + registry_.get().getLedgerMaster().getCompleteLedgers(); } auto it = mStreamMaps[sLedger].begin(); while (it != mStreamMaps[sLedger].end()) { - InfoSub::pointer p = it->second.lock(); + InfoSub::pointer const p = it->second.lock(); if (p) { p->send(jvObj, true); ++it; } else + { it = mStreamMaps[sLedger].erase(it); + } } } if (!mStreamMaps[sBookChanges].empty()) { - Json::Value jvObj = xrpl::RPC::computeBookChanges(lpAccepted); + Json::Value const jvObj = xrpl::RPC::computeBookChanges(lpAccepted); auto it = mStreamMaps[sBookChanges].begin(); while (it != mStreamMaps[sBookChanges].end()) { - InfoSub::pointer p = it->second.lock(); + InfoSub::pointer const p = it->second.lock(); if (p) { p->send(jvObj, true); ++it; } else + { it = mStreamMaps[sBookChanges].erase(it); + } } } @@ -3011,10 +3076,10 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) void NetworkOPsImp::reportFeeChange() { - ServerFeeSummary f{ - registry_.openLedger().current()->fees().base, - registry_.getTxQ().getMetrics(*registry_.openLedger().current()), - registry_.getFeeTrack()}; + ServerFeeSummary const f{ + registry_.get().getOpenLedger().current()->fees().base, + registry_.get().getTxQ().getMetrics(*registry_.get().getOpenLedger().current()), + registry_.get().getFeeTrack()}; // only schedule the job if something has changed if (f != mLastFeeSummary) @@ -3075,7 +3140,7 @@ NetworkOPsImp::transJson( lookup.second && lookup.second->isFieldPresent(sfTransactionIndex)) { uint32_t const txnSeq = lookup.second->getFieldU32(sfTransactionIndex); - uint32_t netID = registry_.getNetworkIDService().getNetworkID(); + uint32_t netID = registry_.get().getNetworkIDService().getNetworkID(); if (transaction->isFieldPresent(sfNetworkID)) netID = transaction->getFieldU32(sfNetworkID); @@ -3114,8 +3179,8 @@ NetworkOPsImp::transJson( // If the offer create is not self funded then add the owner balance if (account != amount.issue().account) { - auto const ownerFunds = - accountFunds(*ledger, account, amount, fhIGNORE_FREEZE, registry_.journal("View")); + auto const ownerFunds = accountFunds( + *ledger, account, amount, fhIGNORE_FREEZE, registry_.get().getJournal("View")); jvObj[jss::transaction][jss::owner_funds] = ownerFunds.getText(); } } @@ -3155,7 +3220,7 @@ NetworkOPsImp::pubValidatedTransaction( MultiApiJson jvObj = transJson(stTxn, trResult, true, ledger, metaRef); { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); auto it = mStreamMaps[sTransactions].begin(); while (it != mStreamMaps[sTransactions].end()) @@ -3170,7 +3235,9 @@ NetworkOPsImp::pubValidatedTransaction( ++it; } else + { it = mStreamMaps[sTransactions].erase(it); + } } it = mStreamMaps[sRTTransactions].begin(); @@ -3187,12 +3254,14 @@ NetworkOPsImp::pubValidatedTransaction( ++it; } else + { it = mStreamMaps[sRTTransactions].erase(it); + } } } if (transaction.getResult() == tesSUCCESS) - registry_.getOrderBookDB().processTxn(ledger, transaction, jvObj); + registry_.get().getOrderBookDB().processTxn(ledger, transaction, jvObj); pubAccountTransaction(ledger, transaction, last); } @@ -3210,7 +3279,7 @@ NetworkOPsImp::pubAccountTransaction( std::vector accountHistoryNotify; auto const currLedgerSeq = ledger->seq(); { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); if (!mSubAccount.empty() || !mSubRTAccount.empty() || !mSubAccountHistory.empty()) { @@ -3223,7 +3292,7 @@ NetworkOPsImp::pubAccountTransaction( while (it != simiIt->second.end()) { - InfoSub::pointer p = it->second.lock(); + InfoSub::pointer const p = it->second.lock(); if (p) { @@ -3232,7 +3301,9 @@ NetworkOPsImp::pubAccountTransaction( ++iProposed; } else + { it = simiIt->second.erase(it); + } } } @@ -3241,7 +3312,7 @@ NetworkOPsImp::pubAccountTransaction( auto it = simiIt->second.begin(); while (it != simiIt->second.end()) { - InfoSub::pointer p = it->second.lock(); + InfoSub::pointer const p = it->second.lock(); if (p) { @@ -3250,7 +3321,9 @@ NetworkOPsImp::pubAccountTransaction( ++iAccepted; } else + { it = simiIt->second.erase(it); + } } } @@ -3339,7 +3412,7 @@ NetworkOPsImp::pubProposedAccountTransaction( std::vector accountHistoryNotify; { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); if (mSubRTAccount.empty()) return; @@ -3355,7 +3428,7 @@ NetworkOPsImp::pubProposedAccountTransaction( while (it != simiIt->second.end()) { - InfoSub::pointer p = it->second.lock(); + InfoSub::pointer const p = it->second.lock(); if (p) { @@ -3364,7 +3437,9 @@ NetworkOPsImp::pubProposedAccountTransaction( ++iProposed; } else + { it = simiIt->second.erase(it); + } } } } @@ -3379,9 +3454,11 @@ NetworkOPsImp::pubProposedAccountTransaction( MultiApiJson jvObj = transJson(tx, result, false, ledger, std::nullopt); for (InfoSub::ref isrListener : notify) + { jvObj.visit( isrListener->getApiVersion(), // [&](Json::Value const& jv) { isrListener->send(jv, true); }); + } XRPL_ASSERT( jvObj.isMember(jss::account_history_tx_stream) == MultiApiJson::none, @@ -3419,7 +3496,7 @@ NetworkOPsImp::subAccount( isrListener->insertSubAccountInfo(naAccountID, rt); } - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); for (auto const& naAccountID : vnaAccountIDs) { @@ -3462,7 +3539,7 @@ NetworkOPsImp::unsubAccountInternal( hash_set const& vnaAccountIDs, bool rt) { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); SubInfoMapType& subMap = rt ? mSubRTAccount : mSubAccount; @@ -3491,7 +3568,7 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo) static auto const databaseType = [&]() -> DatabaseType { // Use a dynamic_cast to return DatabaseType::None // on failure. - if (dynamic_cast(®istry_.getRelationalDatabase())) + if (dynamic_cast(®istry_.get().getRelationalDatabase())) { return DatabaseType::Sqlite; } @@ -3513,7 +3590,7 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo) // LCOV_EXCL_STOP } - registry_.getJobQueue().addJob( + registry_.get().getJobQueue().addJob( jtCLIENT_ACCT_HIST, "HistTxStream", [this, dbType = databaseType, subInfo]() { auto const& accountId = subInfo.index_->accountId_; auto& lastLedgerSeq = subInfo.index_->historyLastLedgerSeq_; @@ -3594,10 +3671,10 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo) switch (dbType) { case Sqlite: { - auto db = static_cast(®istry_.getRelationalDatabase()); - RelationalDatabase::AccountTxPageOptions options{ - accountId, minLedger, maxLedger, marker, 0, true}; - return db->newestAccountTxPage(options); + auto& db = registry_.get().getRelationalDatabase(); + RelationalDatabase::AccountTxPageOptions const options{ + accountId, {minLedger, maxLedger}, marker, 0, true}; + return db.newestAccountTxPage(options); } // LCOV_EXCL_START default: { @@ -3639,7 +3716,8 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo) std::uint32_t validatedMin = UINT_MAX; std::uint32_t validatedMax = 0; auto haveSomeValidatedLedgers = - registry_.getLedgerMaster().getValidatedRange(validatedMin, validatedMax); + registry_.get().getLedgerMaster().getValidatedRange( + validatedMin, validatedMax); return haveSomeValidatedLedgers && validatedMin <= startLedgerSeq && lastLedgerSeq <= validatedMax; @@ -3673,7 +3751,7 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo) auto const& txns = dbResult->first; marker = dbResult->second; - size_t num_txns = txns.size(); + size_t const num_txns = txns.size(); for (size_t i = 0; i < num_txns; ++i) { auto const& [tx, meta] = txns[i]; @@ -3686,7 +3764,7 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo) return; } auto curTxLedger = - registry_.getLedgerMaster().getLedgerBySeq(tx->getLedger()); + registry_.get().getLedgerMaster().getLedgerBySeq(tx->getLedger()); if (!curTxLedger) { // LCOV_EXCL_START @@ -3699,7 +3777,7 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo) return; // LCOV_EXCL_STOP } - std::shared_ptr stTxn = tx->getSTransaction(); + std::shared_ptr const stTxn = tx->getSTransaction(); if (!stTxn) { // LCOV_EXCL_START @@ -3732,10 +3810,8 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo) << " done, found last tx."; return; } - else - { - sendMultiApiJson(jvTx, false); - } + + sendMultiApiJson(jvTx, false); } if (marker) @@ -3820,7 +3896,7 @@ NetworkOPsImp::subAccountHistory(InfoSub::ref isrListener, AccountID const& acco return rpcINVALID_PARAMS; } - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); SubAccountHistoryInfoWeak ahi{isrListener, std::make_shared(accountId)}; auto simIterator = mSubAccountHistory.find(accountId); if (simIterator == mSubAccountHistory.end()) @@ -3834,7 +3910,7 @@ NetworkOPsImp::subAccountHistory(InfoSub::ref isrListener, AccountID const& acco simIterator->second.emplace(isrListener->getSeq(), ahi); } - auto const ledger = registry_.getLedgerMaster().getValidatedLedger(); + auto const ledger = registry_.get().getLedgerMaster().getValidatedLedger(); if (ledger) { subAccountHistoryStart(ledger, ahi); @@ -3867,7 +3943,7 @@ NetworkOPsImp::unsubAccountHistoryInternal( AccountID const& account, bool historyOnly) { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); auto simIterator = mSubAccountHistory.find(account); if (simIterator != mSubAccountHistory.end()) { @@ -3894,8 +3970,10 @@ NetworkOPsImp::unsubAccountHistoryInternal( bool NetworkOPsImp::subBook(InfoSub::ref isrListener, Book const& book) { - if (auto listeners = registry_.getOrderBookDB().makeBookListeners(book)) + if (auto listeners = registry_.get().getOrderBookDB().makeBookListeners(book)) + { listeners->addSubscriber(isrListener); + } else { // LCOV_EXCL_START @@ -3908,7 +3986,7 @@ NetworkOPsImp::subBook(InfoSub::ref isrListener, Book const& book) bool NetworkOPsImp::unsubBook(std::uint64_t uSeq, Book const& book) { - if (auto listeners = registry_.getOrderBookDB().getBookListeners(book)) + if (auto listeners = registry_.get().getOrderBookDB().getBookListeners(book)) listeners->removeSubscriber(uSeq); return true; @@ -3927,7 +4005,7 @@ NetworkOPsImp::acceptLedger(std::optional consensusDe // FIXME Could we improve on this and remove the need for a specialized // API in Consensus? beginConsensus(m_ledgerMaster.getClosedLedger()->header().hash, {}); - mConsensus.simulate(registry_.timeKeeper().closeTime(), consensusDelay); + mConsensus.simulate(registry_.get().getTimeKeeper().closeTime(), consensusDelay); return m_ledgerMaster.getCurrentLedger()->header().seq; } @@ -3942,19 +4020,19 @@ NetworkOPsImp::subLedger(InfoSub::ref isrListener, Json::Value& jvResult) jvResult[jss::ledger_time] = Json::Value::UInt(lpClosed->header().closeTime.time_since_epoch().count()); if (!lpClosed->rules().enabled(featureXRPFees)) - jvResult[jss::fee_ref] = Config::FEE_UNITS_DEPRECATED; + jvResult[jss::fee_ref] = FEE_UNITS_DEPRECATED; jvResult[jss::fee_base] = lpClosed->fees().base.jsonClipped(); jvResult[jss::reserve_base] = lpClosed->fees().reserve.jsonClipped(); jvResult[jss::reserve_inc] = lpClosed->fees().increment.jsonClipped(); - jvResult[jss::network_id] = registry_.getNetworkIDService().getNetworkID(); + jvResult[jss::network_id] = registry_.get().getNetworkIDService().getNetworkID(); } if ((mMode >= OperatingMode::SYNCING) && !isNeedNetworkLedger()) { - jvResult[jss::validated_ledgers] = registry_.getLedgerMaster().getCompleteLedgers(); + jvResult[jss::validated_ledgers] = registry_.get().getLedgerMaster().getCompleteLedgers(); } - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); return mStreamMaps[sLedger].emplace(isrListener->getSeq(), isrListener).second; } @@ -3962,7 +4040,7 @@ NetworkOPsImp::subLedger(InfoSub::ref isrListener, Json::Value& jvResult) bool NetworkOPsImp::subBookChanges(InfoSub::ref isrListener) { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); return mStreamMaps[sBookChanges].emplace(isrListener->getSeq(), isrListener).second; } @@ -3970,23 +4048,23 @@ NetworkOPsImp::subBookChanges(InfoSub::ref isrListener) bool NetworkOPsImp::unsubLedger(std::uint64_t uSeq) { - std::lock_guard sl(mSubLock); - return mStreamMaps[sLedger].erase(uSeq); + std::lock_guard const sl(mSubLock); + return mStreamMaps[sLedger].erase(uSeq) != 0u; } // <-- bool: true=erased, false=was not there bool NetworkOPsImp::unsubBookChanges(std::uint64_t uSeq) { - std::lock_guard sl(mSubLock); - return mStreamMaps[sBookChanges].erase(uSeq); + std::lock_guard const sl(mSubLock); + return mStreamMaps[sBookChanges].erase(uSeq) != 0u; } // <-- bool: true=added, false=already there bool NetworkOPsImp::subManifests(InfoSub::ref isrListener) { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); return mStreamMaps[sManifests].emplace(isrListener->getSeq(), isrListener).second; } @@ -3994,8 +4072,8 @@ NetworkOPsImp::subManifests(InfoSub::ref isrListener) bool NetworkOPsImp::unsubManifests(std::uint64_t uSeq) { - std::lock_guard sl(mSubLock); - return mStreamMaps[sManifests].erase(uSeq); + std::lock_guard const sl(mSubLock); + return mStreamMaps[sManifests].erase(uSeq) != 0u; } // <-- bool: true=added, false=already there @@ -4010,16 +4088,16 @@ NetworkOPsImp::subServer(InfoSub::ref isrListener, Json::Value& jvResult, bool a // CHECKME: is it necessary to provide a random number here? beast::rngfill(uRandom.begin(), uRandom.size(), crypto_prng()); - auto const& feeTrack = registry_.getFeeTrack(); + auto const& feeTrack = registry_.get().getFeeTrack(); jvResult[jss::random] = to_string(uRandom); jvResult[jss::server_status] = strOperatingMode(admin); jvResult[jss::load_base] = feeTrack.getLoadBase(); jvResult[jss::load_factor] = feeTrack.getLoadFactor(); jvResult[jss::hostid] = getHostId(admin); jvResult[jss::pubkey_node] = - toBase58(TokenType::NodePublic, registry_.app().nodeIdentity().first); + toBase58(TokenType::NodePublic, registry_.get().getApp().nodeIdentity().first); - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); return mStreamMaps[sServer].emplace(isrListener->getSeq(), isrListener).second; } @@ -4027,15 +4105,15 @@ NetworkOPsImp::subServer(InfoSub::ref isrListener, Json::Value& jvResult, bool a bool NetworkOPsImp::unsubServer(std::uint64_t uSeq) { - std::lock_guard sl(mSubLock); - return mStreamMaps[sServer].erase(uSeq); + std::lock_guard const sl(mSubLock); + return mStreamMaps[sServer].erase(uSeq) != 0u; } // <-- bool: true=added, false=already there bool NetworkOPsImp::subTransactions(InfoSub::ref isrListener) { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); return mStreamMaps[sTransactions].emplace(isrListener->getSeq(), isrListener).second; } @@ -4043,15 +4121,15 @@ NetworkOPsImp::subTransactions(InfoSub::ref isrListener) bool NetworkOPsImp::unsubTransactions(std::uint64_t uSeq) { - std::lock_guard sl(mSubLock); - return mStreamMaps[sTransactions].erase(uSeq); + std::lock_guard const sl(mSubLock); + return mStreamMaps[sTransactions].erase(uSeq) != 0u; } // <-- bool: true=added, false=already there bool NetworkOPsImp::subRTTransactions(InfoSub::ref isrListener) { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); return mStreamMaps[sRTTransactions].emplace(isrListener->getSeq(), isrListener).second; } @@ -4059,15 +4137,15 @@ NetworkOPsImp::subRTTransactions(InfoSub::ref isrListener) bool NetworkOPsImp::unsubRTTransactions(std::uint64_t uSeq) { - std::lock_guard sl(mSubLock); - return mStreamMaps[sRTTransactions].erase(uSeq); + std::lock_guard const sl(mSubLock); + return mStreamMaps[sRTTransactions].erase(uSeq) != 0u; } // <-- bool: true=added, false=already there bool NetworkOPsImp::subValidations(InfoSub::ref isrListener) { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); return mStreamMaps[sValidations].emplace(isrListener->getSeq(), isrListener).second; } @@ -4081,15 +4159,15 @@ NetworkOPsImp::stateAccounting(Json::Value& obj) bool NetworkOPsImp::unsubValidations(std::uint64_t uSeq) { - std::lock_guard sl(mSubLock); - return mStreamMaps[sValidations].erase(uSeq); + std::lock_guard const sl(mSubLock); + return mStreamMaps[sValidations].erase(uSeq) != 0u; } // <-- bool: true=added, false=already there bool NetworkOPsImp::subPeerStatus(InfoSub::ref isrListener) { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); return mStreamMaps[sPeerStatus].emplace(isrListener->getSeq(), isrListener).second; } @@ -4097,15 +4175,15 @@ NetworkOPsImp::subPeerStatus(InfoSub::ref isrListener) bool NetworkOPsImp::unsubPeerStatus(std::uint64_t uSeq) { - std::lock_guard sl(mSubLock); - return mStreamMaps[sPeerStatus].erase(uSeq); + std::lock_guard const sl(mSubLock); + return mStreamMaps[sPeerStatus].erase(uSeq) != 0u; } // <-- bool: true=added, false=already there bool NetworkOPsImp::subConsensus(InfoSub::ref isrListener) { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); return mStreamMaps[sConsensusPhase].emplace(isrListener->getSeq(), isrListener).second; } @@ -4113,16 +4191,16 @@ NetworkOPsImp::subConsensus(InfoSub::ref isrListener) bool NetworkOPsImp::unsubConsensus(std::uint64_t uSeq) { - std::lock_guard sl(mSubLock); - return mStreamMaps[sConsensusPhase].erase(uSeq); + std::lock_guard const sl(mSubLock); + return mStreamMaps[sConsensusPhase].erase(uSeq) != 0u; } InfoSub::pointer NetworkOPsImp::findRpcSub(std::string const& strUrl) { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); - subRpcMapType::iterator it = mRpcSubMap.find(strUrl); + subRpcMapType::iterator const it = mRpcSubMap.find(strUrl); if (it != mRpcSubMap.end()) return it->second; @@ -4133,7 +4211,7 @@ NetworkOPsImp::findRpcSub(std::string const& strUrl) InfoSub::pointer NetworkOPsImp::addRpcSub(std::string const& strUrl, InfoSub::ref rspEntry) { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); mRpcSubMap.emplace(strUrl, rspEntry); @@ -4143,7 +4221,7 @@ NetworkOPsImp::addRpcSub(std::string const& strUrl, InfoSub::ref rspEntry) bool NetworkOPsImp::tryRemoveRpcSub(std::string const& strUrl) { - std::lock_guard sl(mSubLock); + std::lock_guard const sl(mSubLock); auto pInfo = findRpcSub(strUrl); if (!pInfo) @@ -4153,7 +4231,7 @@ NetworkOPsImp::tryRemoveRpcSub(std::string const& strUrl) // this entry before removing for (SubMapType const& map : mStreamMaps) { - if (map.find(pInfo->getSeq()) != map.end()) + if (map.contains(pInfo->getSeq())) return false; } mRpcSubMap.erase(strUrl); @@ -4200,11 +4278,11 @@ NetworkOPsImp::getBookPage( std::shared_ptr sleOfferDir; uint256 offerIndex; - unsigned int uBookEntry; + unsigned int uBookEntry = 0; STAmount saDirRate; auto const rate = transferRate(view, book.out.account); - auto viewJ = registry_.journal("View"); + auto viewJ = registry_.get().getJournal("View"); while (!bDone && iLimit-- > 0) { @@ -4216,9 +4294,13 @@ NetworkOPsImp::getBookPage( auto const ledgerIndex = view.succ(uTipIndex, uBookEnd); if (ledgerIndex) + { sleOfferDir = view.read(keylet::page(*ledgerIndex)); + } else + { sleOfferDir.reset(); + } if (!sleOfferDir) { @@ -4327,7 +4409,7 @@ NetworkOPsImp::getBookPage( .setJson(jvOffer[jss::taker_pays_funded]); } - STAmount saOwnerPays = (parityRate == offerRate) + STAmount const saOwnerPays = (parityRate == offerRate) ? saTakerGetsFunded : std::min(saOwnerFunds, multiply(saTakerGetsFunded, offerRate)); @@ -4500,7 +4582,7 @@ NetworkOPsImp::collect_metrics() std::chrono::steady_clock::now() - start); counters[static_cast(mode)].dur += current; - std::lock_guard lock(m_statsMutex); + std::lock_guard const lock(m_statsMutex); m_stats.disconnected_duration.set( counters[static_cast(OperatingMode::DISCONNECTED)].dur.count()); m_stats.connected_duration.set( @@ -4528,7 +4610,7 @@ NetworkOPsImp::StateAccounting::mode(OperatingMode om) { auto now = std::chrono::steady_clock::now(); - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); ++counters_[static_cast(om)].transitions; if (om == OperatingMode::FULL && counters_[static_cast(om)].transitions == 1) { @@ -4561,7 +4643,7 @@ NetworkOPsImp::StateAccounting::json(Json::Value& obj) const state[jss::duration_us] = std::to_string(counters[i].dur.count()); } obj[jss::server_state_duration_us] = std::to_string(current.count()); - if (initialSync) + if (initialSync != 0u) obj[jss::initial_sync_duration_us] = std::to_string(initialSync); } @@ -4573,11 +4655,11 @@ make_NetworkOPs( NetworkOPs::clock_type& clock, bool standalone, std::size_t minPeerCount, - bool startvalid, - JobQueue& job_queue, + bool startValid, + JobQueue& jobQueue, LedgerMaster& ledgerMaster, ValidatorKeys const& validatorKeys, - boost::asio::io_context& io_svc, + boost::asio::io_context& ioCtx, beast::Journal journal, beast::insight::Collector::ptr const& collector) { @@ -4586,11 +4668,11 @@ make_NetworkOPs( clock, standalone, minPeerCount, - startvalid, - job_queue, + startValid, + jobQueue, ledgerMaster, validatorKeys, - io_svc, + ioCtx, journal, collector); } diff --git a/src/xrpld/app/misc/README.md b/src/xrpld/app/misc/README.md index 2f9fff0ca3..359f58578d 100644 --- a/src/xrpld/app/misc/README.md +++ b/src/xrpld/app/misc/README.md @@ -1,6 +1,6 @@ # Fee Voting -The Ripple payment protocol enforces a fee schedule expressed in units of the +The XRPL payment protocol enforces a fee schedule expressed in units of the native currency, XRP. Fees for transactions are paid directly from the account owner. There are also reserve requirements for each item that occupies storage in the ledger. The reserve fee schedule contains both a per-account reserve, @@ -20,7 +20,7 @@ subsequent ledgers a new fee schedule is enacted. ## Consensus -The Ripple consensus algorithm allows distributed participants to arrive at +The XRPL consensus algorithm allows distributed participants to arrive at the same answer for yes/no questions. The canonical case for consensus is whether or not a particular transaction is included in the ledger. Fees present a more difficult challenge, since the decision on the new fee is not @@ -54,7 +54,7 @@ be converged in the consensus process, the following algorithm is used: ## Configuration -A validating instance of rippled uses information in the configuration file +A validating instance of xrpld uses information in the configuration file to determine how it wants to vote on the fee schedule. It is the responsibility of the administrator to set these values. @@ -64,7 +64,7 @@ of the administrator to set these values. An Amendment is a new or proposed change to a ledger rule. Ledger rules affect transaction processing and consensus; peers must use the same set of rules for -consensus to succeed, otherwise different instances of rippled will get +consensus to succeed, otherwise different instances of xrpld will get different results. Amendments can be almost anything but they must be accepted by a network majority through a consensus process before they are utilized. An Amendment must receive at least an 80% approval rate from validating nodes for @@ -77,7 +77,7 @@ process of an Amendment from its conception to approval and usage. - Some members contribute their time and work to develop the Amendment. -- A pull request is created and the new code is folded into a rippled build +- A pull request is created and the new code is folded into an xrpld build and made available for use. - The consensus process begins with the validating nodes. diff --git a/src/xrpld/app/misc/SHAMapStore.h b/src/xrpld/app/misc/SHAMapStore.h index 67477593cb..b377538f62 100644 --- a/src/xrpld/app/misc/SHAMapStore.h +++ b/src/xrpld/app/misc/SHAMapStore.h @@ -1,7 +1,6 @@ #pragma once -#include - +#include #include #include diff --git a/src/xrpld/app/misc/SHAMapStoreImp.cpp b/src/xrpld/app/misc/SHAMapStoreImp.cpp index 509aae1640..7bc3b31169 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.cpp +++ b/src/xrpld/app/misc/SHAMapStoreImp.cpp @@ -16,14 +16,14 @@ namespace xrpl { void SHAMapStoreImp::SavedStateDB::init(BasicConfig const& config, std::string const& dbName) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); initStateDB(sqlDb_, config, dbName); } LedgerIndex SHAMapStoreImp::SavedStateDB::getCanDelete() { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return xrpl::getCanDelete(sqlDb_); } @@ -31,7 +31,7 @@ SHAMapStoreImp::SavedStateDB::getCanDelete() LedgerIndex SHAMapStoreImp::SavedStateDB::setCanDelete(LedgerIndex canDelete) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return xrpl::setCanDelete(sqlDb_, canDelete); } @@ -39,7 +39,7 @@ SHAMapStoreImp::SavedStateDB::setCanDelete(LedgerIndex canDelete) SavedState SHAMapStoreImp::SavedStateDB::getState() { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return xrpl::getSavedState(sqlDb_); } @@ -47,14 +47,14 @@ SHAMapStoreImp::SavedStateDB::getState() void SHAMapStoreImp::SavedStateDB::setState(SavedState const& state) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); xrpl::setSavedState(sqlDb_, state); } void SHAMapStoreImp::SavedStateDB::setLastRotated(LedgerIndex seq) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); xrpl::setLastRotated(sqlDb_, seq); } @@ -93,11 +93,11 @@ SHAMapStoreImp::SHAMapStoreImp( get_if_exists(section, "online_delete", deleteInterval_); - if (deleteInterval_) + if (deleteInterval_ != 0u) { // Configuration that affects the behavior of online delete get_if_exists(section, "delete_batch", deleteBatch_); - std::uint32_t temp; + std::uint32_t temp = 0; if (get_if_exists(section, "back_off_milliseconds", temp) || // Included for backward compatibility with an undocumented setting get_if_exists(section, "backOff", temp)) @@ -138,12 +138,12 @@ SHAMapStoreImp::makeNodeStore(int readThreads) auto nscfg = app_.config().section(ConfigSection::nodeDatabase()); std::unique_ptr db; - if (deleteInterval_) + if (deleteInterval_ != 0u) { SavedState state = state_db_.getState(); auto writableBackend = makeBackendRotating(state.writableDb); auto archiveBackend = makeBackendRotating(state.archiveDb); - if (!state.writableDb.size()) + if (state.writableDb.empty()) { state.writableDb = writableBackend->getName(); state.archiveDb = archiveBackend->getName(); @@ -158,7 +158,7 @@ SHAMapStoreImp::makeNodeStore(int readThreads) std::move(writableBackend), std::move(archiveBackend), nscfg, - app_.logs().journal(nodeStoreName_)); + app_.getJournal(nodeStoreName_)); fdRequired_ += dbr->fdRequired(); dbRotating_ = dbr.get(); db.reset(dynamic_cast(dbr.release())); @@ -170,7 +170,7 @@ SHAMapStoreImp::makeNodeStore(int readThreads) scheduler_, readThreads, nscfg, - app_.logs().journal(nodeStoreName_)); + app_.getJournal(nodeStoreName_)); fdRequired_ += db->fdRequired(); } return db; @@ -180,7 +180,7 @@ void SHAMapStoreImp::onLedgerClosed(std::shared_ptr const& ledger) { { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); newLedger_ = ledger; working_ = true; } @@ -209,7 +209,7 @@ SHAMapStoreImp::copyNode(std::uint64_t& nodeCount, SHAMapTreeNode const& node) // Copy a single record from node to dbRotating_ dbRotating_->fetchNodeObject( node.getHash().as_uint256(), 0, NodeStore::FetchType::synchronous, true); - if (!(++nodeCount % checkHealthInterval_)) + if ((++nodeCount % checkHealthInterval_) == 0u) { if (healthWait() == stopping) return false; @@ -248,11 +248,13 @@ SHAMapStoreImp::run() validatedLedger = std::move(newLedger_); } else + { continue; + } } LedgerIndex const validatedSeq = validatedLedger->header().seq; - if (!lastRotated) + if (lastRotated == 0u) { lastRotated = validatedSeq; state_db_.setLastRotated(lastRotated); @@ -356,7 +358,7 @@ SHAMapStoreImp::run() void SHAMapStoreImp::dbPaths() { - Section section{app_.config().section(ConfigSection::nodeDatabase())}; + Section const section{app_.config().section(ConfigSection::nodeDatabase())}; boost::filesystem::path dbPath = get(section, "path"); if (boost::filesystem::exists(dbPath)) @@ -404,16 +406,22 @@ SHAMapStoreImp::dbPaths() it != boost::filesystem::directory_iterator(); ++it) { - if (!state.writableDb.compare(it->path().string())) + if (state.writableDb.compare(it->path().string()) == 0) + { writableDbExists = true; - else if (!state.archiveDb.compare(it->path().string())) + } + else if (state.archiveDb.compare(it->path().string()) == 0) + { archiveDbExists = true; - else if (!dbPrefix_.compare(it->path().stem().string())) + } + else if (dbPrefix_.compare(it->path().stem().string()) == 0) + { pathsToDelete.push_back(it->path()); + } } - if ((!writableDbExists && state.writableDb.size()) || - (!archiveDbExists && state.archiveDb.size()) || (writableDbExists != archiveDbExists) || + if ((!writableDbExists && !state.writableDb.empty()) || + (!archiveDbExists && !state.archiveDb.empty()) || (writableDbExists != archiveDbExists) || state.writableDb.empty() != state.archiveDb.empty()) { boost::filesystem::path stateDbPathName = app_.config().legacy("database_path"); @@ -438,7 +446,7 @@ SHAMapStoreImp::dbPaths() } // The necessary directories exist. Now, remove any others. - for (boost::filesystem::path& p : pathsToDelete) + for (boost::filesystem::path const& p : pathsToDelete) boost::filesystem::remove_all(p); } @@ -448,7 +456,7 @@ SHAMapStoreImp::makeBackendRotating(std::string path) Section section{app_.config().section(ConfigSection::nodeDatabase())}; boost::filesystem::path newPath; - if (path.size()) + if (!path.empty()) { newPath = path; } @@ -465,7 +473,7 @@ SHAMapStoreImp::makeBackendRotating(std::string path) section, megabytes(app_.config().getValueFor(SizedItem::burstSize, std::nullopt)), scheduler_, - app_.logs().journal(nodeStoreName_))}; + app_.getJournal(nodeStoreName_))}; backend->open(); return backend; } @@ -625,7 +633,7 @@ SHAMapStoreImp::stop() if (thread_.joinable()) { { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); stop_ = true; cond_.notify_one(); } @@ -638,7 +646,7 @@ SHAMapStoreImp::minimumOnline() const { // minimumOnline_ with 0 value is equivalent to unknown/not set. // Don't attempt to acquire ledgers if that value is unknown. - if (deleteInterval_ && minimumOnline_) + if ((deleteInterval_ != 0u) && (minimumOnline_ != 0u)) return minimumOnline_.load(); return app_.getLedgerMaster().minSqlSeq(); } diff --git a/src/xrpld/app/misc/SHAMapStoreImp.h b/src/xrpld/app/misc/SHAMapStoreImp.h index b73b0b8d16..3056a93dd3 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.h +++ b/src/xrpld/app/misc/SHAMapStoreImp.h @@ -200,7 +200,7 @@ private: clearPrior(LedgerIndex lastRotated); /** - * This is a health check for online deletion that waits until rippled is + * This is a health check for online deletion that waits until xrpld is * stable before returning. It returns an indication of whether the server * is stopping. * diff --git a/src/xrpld/app/misc/TxQ.h b/src/xrpld/app/misc/TxQ.h index 4f02d6d617..b9ea7cc8f6 100644 --- a/src/xrpld/app/misc/TxQ.h +++ b/src/xrpld/app/misc/TxQ.h @@ -146,23 +146,23 @@ public: explicit Metrics() = default; /// Number of transactions in the queue - std::size_t txCount; + std::size_t txCount{}; /// Max transactions currently allowed in queue std::optional txQMaxSize; /// Number of transactions currently in the open ledger - std::size_t txInLedger; + std::size_t txInLedger{}; /// Number of transactions expected per ledger - std::size_t txPerLedger; + std::size_t txPerLedger{}; /// Reference transaction fee level - FeeLevel64 referenceFeeLevel; + FeeLevel64 referenceFeeLevel{}; /// Minimum fee level for a transaction to be considered for /// the open ledger or the queue - FeeLevel64 minProcessingFeeLevel; + FeeLevel64 minProcessingFeeLevel{}; /// Median fee level of the last ledger - FeeLevel64 medFeeLevel; + FeeLevel64 medFeeLevel{}; /// Minimum fee level to get into the current open ledger, /// bypassing the queue - FeeLevel64 openLedgerFeeLevel; + FeeLevel64 openLedgerFeeLevel{}; }; /** @@ -398,9 +398,9 @@ private: Updates fee metrics based on the transactions in the ReadView for use in fee escalation calculations. - @param app Rippled Application object. + @param app Xrpld Application object. @param view View of the LCL that was just closed or received. - @param timeLeap Indicates that rippled is under load so fees + @param timeLeap Indicates that xrpld is under load so fees should grow faster. @param setup Customization params. */ @@ -511,7 +511,7 @@ private: their `retriesRemaining` forced down as part of the penalty. */ - int retriesRemaining; + int retriesRemaining{retriesAllowed}; /// Flags provided to `apply`. If the transaction is later /// attempted with different flags, it will need to be /// `preflight`ed again. @@ -693,12 +693,12 @@ private: }; // Helper function returns requiredFeeLevel. - FeeLevel64 + static FeeLevel64 getRequiredFeeLevel( OpenView& view, ApplyFlags flags, FeeMetrics::Snapshot const& metricsSnapshot, - std::lock_guard const& lock) const; + std::lock_guard const& lock); // Helper function for TxQ::apply. If a transaction's fee is high enough, // attempt to directly apply that transaction to the ledger. diff --git a/src/xrpld/app/misc/ValidatorList.h b/src/xrpld/app/misc/ValidatorList.h index e774da4713..ff5aa8c71f 100644 --- a/src/xrpld/app/misc/ValidatorList.h +++ b/src/xrpld/app/misc/ValidatorList.h @@ -108,11 +108,11 @@ struct ValidatorBlobInfo Trusted Validators List ----------------------- - Rippled accepts ledger proposals and validations from trusted validator + Xrpld accepts ledger proposals and validations from trusted validator nodes. A ledger is considered fully-validated once the number of received trusted validations for a ledger meets or exceeds a quorum value. - This class manages the set of validation public keys the local rippled node + This class manages the set of validation public keys the local xrpld node trusts. The list of trusted keys is populated using the keys listed in the configuration file as well as lists signed by trusted publishers. The trusted publisher public keys are specified in the config. @@ -121,9 +121,9 @@ struct ValidatorBlobInfo @li @c "blob": Base64-encoded JSON string containing a @c "sequence", @c "validFrom", @c "validUntil", and @c "validators" field. @c "validFrom" - contains the Ripple timestamp (seconds since January 1st, 2000 (00:00 + contains the XRPL timestamp (seconds since January 1st, 2000 (00:00 UTC)) for when the list becomes valid. @c "validUntil" contains the - Ripple timestamp for when the list expires. @c "validators" contains + XRPL timestamp for when the list expires. @c "validators" contains an array of objects with a @c "validation_public_key" and optional @c "manifest" field. @c "validation_public_key" should be the hex-encoded master public key. @c "manifest" should be the @@ -157,7 +157,7 @@ class ValidatorList std::vector list; std::vector manifests; - std::size_t sequence; + std::size_t sequence{}; TimeKeeper::time_point validFrom; TimeKeeper::time_point validUntil; std::string siteUri; @@ -173,7 +173,7 @@ class ValidatorList struct PublisherListCollection { - PublisherStatus status; + PublisherStatus status = PublisherStatus::unavailable; /* The `current` VL is the one which 1. Has the largest sequence number that @@ -223,7 +223,7 @@ class ValidatorList hash_set trustedMasterKeys_; // Minimum number of lists on which a trusted validator must appear on - std::size_t listThreshold_; + std::size_t listThreshold_{1}; // The current list of trusted signing keys. For those validators using // a manifest, the signing key is the ephemeral key. For the ones using @@ -646,7 +646,7 @@ public: QuorumKeys getQuorumKeys() const { - shared_lock read_lock{mutex_}; + shared_lock const read_lock{mutex_}; return {quorum_, trustedSigningKeys_}; } diff --git a/src/xrpld/app/misc/ValidatorSite.h b/src/xrpld/app/misc/ValidatorSite.h index 9c2d45fb6d..ab82ae168d 100644 --- a/src/xrpld/app/misc/ValidatorSite.h +++ b/src/xrpld/app/misc/ValidatorSite.h @@ -28,7 +28,7 @@ namespace xrpl { @li @c "blob": Base64-encoded JSON string containing a @c "sequence", @c "validUntil", and @c "validators" field. @c "validUntil" contains the - Ripple timestamp (seconds since January 1st, 2000 (00:00 UTC)) for when + XRPL timestamp (seconds since January 1st, 2000 (00:00 UTC)) for when the list expires. @c "validators" contains an array of objects with a @c "validation_public_key" and optional @c "manifest" field. @c "validation_public_key" should be the hex-encoded master public key. @@ -85,12 +85,12 @@ private: /// when we've gotten a temp redirect std::shared_ptr activeResource; - unsigned short redirCount; + unsigned short redirCount{0}; std::chrono::minutes refreshInterval; clock_type::time_point nextRefresh; std::optional lastRefreshStatus; endpoint_type lastRequestEndpoint; - bool lastRequestSuccessful; + bool lastRequestSuccessful{false}; }; Application& app_; @@ -197,7 +197,7 @@ private: onSiteFetch( boost::system::error_code const& ec, endpoint_type const& endpoint, - detail::response_type&& res, + detail::response_type const& res, std::size_t siteIdx); /// Store latest list fetched from anywhere @@ -224,7 +224,7 @@ private: /// lock over sites_mutex_ required std::shared_ptr processRedirect( - detail::response_type& res, + detail::response_type const& res, std::size_t siteIdx, std::lock_guard const&); diff --git a/src/xrpld/app/misc/detail/AccountTxPaging.cpp b/src/xrpld/app/misc/detail/AccountTxPaging.cpp index 63457f00ac..bb0a09426a 100644 --- a/src/xrpld/app/misc/detail/AccountTxPaging.cpp +++ b/src/xrpld/app/misc/detail/AccountTxPaging.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -27,13 +28,17 @@ convertBlobsToTxResult( // if properly formed meta is available we can use it to generate ctid if (metaset->getAsObject().isFieldPresent(sfTransactionIndex)) + { tr->setStatus( Transaction::sqlTransactionStatus(status), ledger_index, metaset->getAsObject().getFieldU32(sfTransactionIndex), app.getNetworkIDService().getNetworkID()); + } else + { tr->setStatus(Transaction::sqlTransactionStatus(status), ledger_index); + } to.emplace_back(std::move(tr), metaset); }; diff --git a/src/xrpld/app/misc/detail/AmendmentTable.cpp b/src/xrpld/app/misc/detail/AmendmentTable.cpp index 196584fe6f..afecb08b24 100644 --- a/src/xrpld/app/misc/detail/AmendmentTable.cpp +++ b/src/xrpld/app/misc/detail/AmendmentTable.cpp @@ -40,8 +40,10 @@ parseSection(Section const& section) uint256 id; if (!id.parseHex(match[1])) + { Throw( "Invalid amendment ID '" + match[1] + "' in [" + section.name() + "]"); + } names.push_back(std::make_pair(id, match[2])); } @@ -357,7 +359,7 @@ private: mutable std::mutex mutex_; hash_map amendmentMap_; - std::uint32_t lastUpdateSeq_; + std::uint32_t lastUpdateSeq_{0}; // Record of the last votes seen from trusted validators. TrustedVotes previousTrustedVotes_; @@ -370,7 +372,7 @@ private: std::unique_ptr lastVote_; // True if an unsupported amendment is enabled - bool unsupportedEnabled_; + bool unsupportedEnabled_{false}; // Unset if no unsupported amendments reach majority, // else set to the earliest time an unsupported amendment @@ -477,11 +479,7 @@ AmendmentTableImpl::AmendmentTableImpl( Section const& enabled, Section const& vetoed, beast::Journal journal) - : lastUpdateSeq_(0) - , majorityTime_(majorityTime) - , unsupportedEnabled_(false) - , j_(journal) - , db_(registry.getWalletDB()) + : majorityTime_(majorityTime), j_(journal), db_(registry.getWalletDB()) { std::lock_guard lock(mutex_); @@ -529,11 +527,10 @@ AmendmentTableImpl::AmendmentTableImpl( " in favor of data in db/wallet.db."; break; } - else - { // Otherwise transfer config data into the table - detect_conflict.insert(a.first); - persistVote(a.first, a.second, AmendmentVote::up); - } + + // Otherwise transfer config data into the table + detect_conflict.insert(a.first); + persistVote(a.first, a.second, AmendmentVote::up); } // Parse vetoed amendments from config @@ -545,18 +542,17 @@ AmendmentTableImpl::AmendmentTableImpl( " in favor of data in db/wallet.db."; break; } + + // Otherwise transfer config data into the table + if (!detect_conflict.contains(a.first)) + { + persistVote(a.first, a.second, AmendmentVote::down); + } else - { // Otherwise transfer config data into the table - if (detect_conflict.count(a.first) == 0) - { - persistVote(a.first, a.second, AmendmentVote::down); - } - else - { - JLOG(j_.warn()) << "[veto_amendments] section in config has amendment " << '(' - << a.first << ", " << a.second - << ") both [veto_amendments] and [amendments]."; - } + { + JLOG(j_.warn()) << "[veto_amendments] section in config has amendment " << '(' + << a.first << ", " << a.second + << ") both [veto_amendments] and [amendments]."; } } @@ -636,7 +632,7 @@ AmendmentTableImpl::get(uint256 const& amendmentHash, std::lock_guardvote != AmendmentVote::down) + if ((s == nullptr) || s->vote != AmendmentVote::down) return false; s->vote = AmendmentVote::up; persistVote(amendment, s->name, s->vote); @@ -689,7 +685,7 @@ AmendmentTableImpl::unVeto(uint256 const& amendment) bool AmendmentTableImpl::enable(uint256 const& amendment) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); AmendmentState& s = add(amendment, lock); if (s.enabled) @@ -709,30 +705,30 @@ AmendmentTableImpl::enable(uint256 const& amendment) bool AmendmentTableImpl::isEnabled(uint256 const& amendment) const { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); AmendmentState const* s = get(amendment, lock); - return s && s->enabled; + return (s != nullptr) && s->enabled; } bool AmendmentTableImpl::isSupported(uint256 const& amendment) const { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); AmendmentState const* s = get(amendment, lock); - return s && s->supported; + return (s != nullptr) && s->supported; } bool AmendmentTableImpl::hasUnsupportedEnabled() const { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return unsupportedEnabled_; } std::optional AmendmentTableImpl::firstUnsupportedExpected() const { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return firstUnsupportedExpected_; } @@ -744,12 +740,12 @@ AmendmentTableImpl::doValidation(std::set const& enabled) const std::vector amendments; { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); amendments.reserve(amendmentMap_.size()); for (auto const& e : amendmentMap_) { if (e.second.supported && e.second.vote == AmendmentVote::up && - (enabled.count(e.first) == 0)) + (!enabled.contains(e.first))) { amendments.push_back(e.first); JLOG(j_.info()) << "Voting for amendment " << e.second.name; @@ -782,7 +778,7 @@ AmendmentTableImpl::doVoting( << enabledAmendments.size() << ", " << majorityAmendments.size() << ", " << valSet.size(); - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); // Keep a record of the votes we received. previousTrustedVotes_.recordVotes(rules, valSet, closeTime, j_, lock); @@ -864,7 +860,7 @@ AmendmentTableImpl::doVoting( bool AmendmentTableImpl::needValidatedLedger(LedgerIndex ledgerSeq) const { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); // Is there a ledger in which an amendment could have been enabled // between these two ledger sequences? @@ -881,7 +877,7 @@ AmendmentTableImpl::doValidatedLedger( for (auto& e : enabled) enable(e); - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); // Remember the ledger sequence of this update. lastUpdateSeq_ = ledgerSeq; @@ -892,7 +888,7 @@ AmendmentTableImpl::doValidatedLedger( firstUnsupportedExpected_.reset(); for (auto const& [hash, time] : majority) { - AmendmentState& s = add(hash, lock); + AmendmentState const& s = add(hash, lock); if (s.enabled) continue; @@ -912,7 +908,7 @@ AmendmentTableImpl::doValidatedLedger( void AmendmentTableImpl::trustChanged(hash_set const& allTrusted) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); previousTrustedVotes_.trustChanged(allTrusted, lock); } @@ -931,9 +927,13 @@ AmendmentTableImpl::injectJson( if (!fs.enabled && isAdmin) { if (fs.vote == AmendmentVote::obsolete) + { v[jss::vetoed] = "Obsolete"; + } else + { v[jss::vetoed] = fs.vote == AmendmentVote::down; + } } v[jss::enabled] = fs.enabled; @@ -946,7 +946,7 @@ AmendmentTableImpl::injectJson( v[jss::count] = votesFor; v[jss::validations] = votesTotal; - if (votesNeeded) + if (votesNeeded != 0) v[jss::threshold] = votesNeeded; } } @@ -956,7 +956,7 @@ AmendmentTableImpl::getJson(bool isAdmin) const { Json::Value ret(Json::objectValue); { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); for (auto const& e : amendmentMap_) { injectJson( @@ -972,9 +972,9 @@ AmendmentTableImpl::getJson(uint256 const& amendmentID, bool isAdmin) const Json::Value ret = Json::objectValue; { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); AmendmentState const* a = get(amendmentID, lock); - if (a) + if (a != nullptr) { Json::Value& jAmendment = (ret[to_string(amendmentID)] = Json::objectValue); injectJson(jAmendment, amendmentID, *a, isAdmin, lock); diff --git a/src/xrpld/app/misc/detail/Transaction.cpp b/src/xrpld/app/misc/detail/Transaction.cpp index fae26873fb..f0cabf0fa6 100644 --- a/src/xrpld/app/misc/detail/Transaction.cpp +++ b/src/xrpld/app/misc/detail/Transaction.cpp @@ -16,7 +16,7 @@ Transaction::Transaction( std::shared_ptr const& stx, std::string& reason, Application& app) noexcept - : mTransaction(stx), mApp(app), j_(app.journal("Ledger")) + : mTransaction(stx), mApp(app), j_(app.getJournal("Ledger")) { try { @@ -53,26 +53,26 @@ Transaction::setStatus( TransStatus Transaction::sqlTransactionStatus(boost::optional const& status) { - char const c = (status) ? (*status)[0] : safe_cast(txnSqlUnknown); + auto const c = (status) ? safe_cast((*status)[0]) : TxnSql::txnSqlUnknown; - switch (c) + switch (static_cast(c)) { - case txnSqlNew: + case TxnSql::txnSqlNew: return NEW; - case txnSqlConflict: + case TxnSql::txnSqlConflict: return CONFLICTED; - case txnSqlHeld: + case TxnSql::txnSqlHeld: return HELD; - case txnSqlValidated: + case TxnSql::txnSqlValidated: return COMMITTED; - case txnSqlIncluded: + case TxnSql::txnSqlIncluded: return INCLUDED; + default: + XRPL_ASSERT( + c == TxnSql::txnSqlUnknown, + "xrpl::Transaction::sqlTransactionStatus : unknown transaction status"); } - XRPL_ASSERT( - c == txnSqlUnknown, - "xrpl::Transaction::sqlTransactionStatus : unknown transaction " - "status"); return INVALID; } @@ -133,7 +133,7 @@ Transaction::getJson(JsonOptions options, bool binary) const Json::Value ret(mTransaction->getJson(options & ~JsonOptions::include_date, binary)); // NOTE Binary STTx::getJson output might not be a JSON object - if (ret.isObject() && mLedgerIndex) + if (ret.isObject() && (mLedgerIndex != 0u)) { if (!(options & JsonOptions::disable_API_prior_V2)) { diff --git a/src/xrpld/app/misc/detail/TxQ.cpp b/src/xrpld/app/misc/detail/TxQ.cpp index 718b468ff0..3494a4b7bd 100644 --- a/src/xrpld/app/misc/detail/TxQ.cpp +++ b/src/xrpld/app/misc/detail/TxQ.cpp @@ -20,8 +20,8 @@ static FeeLevel64 getFeeLevelPaid(ReadView const& view, STTx const& tx) { auto const [baseFee, effectiveFeePaid] = [&view, &tx]() { - XRPAmount baseFee = calculateBaseFee(view, tx); - XRPAmount feePaid = tx[sfFee].xrp(); + XRPAmount const baseFee = calculateBaseFee(view, tx); + XRPAmount const feePaid = tx[sfFee].xrp(); // If baseFee is 0 then the cost of a basic transaction is free, but we // need the effective fee level to be non-zero. @@ -121,7 +121,7 @@ TxQ::FeeMetrics::update( txnsExpected_ = std::min(next, maximumTxnCount_.value_or(next)); } - if (!size) + if (size == 0) { escalationMultiplier_ = setup.minimumEscalationMultiplier; } @@ -172,7 +172,7 @@ sumOfFirstSquares(std::size_t xIn) // We expect that size_t == std::uint64_t but, just in case, guarantee // we lose no bits. - std::uint64_t x{xIn}; + std::uint64_t const x{xIn}; // If x is anywhere on the order of 2^^21, it's going // to completely dominate the computation and is likely @@ -185,16 +185,16 @@ sumOfFirstSquares(std::size_t xIn) } // Unit tests for sumOfSquares() -static_assert(sumOfFirstSquares(1).first == true); +static_assert(sumOfFirstSquares(1).first); static_assert(sumOfFirstSquares(1).second == 1); -static_assert(sumOfFirstSquares(2).first == true); +static_assert(sumOfFirstSquares(2).first); static_assert(sumOfFirstSquares(2).second == 5); -static_assert(sumOfFirstSquares(0x1FFFFF).first == true, ""); +static_assert(sumOfFirstSquares(0x1FFFFF).first, ""); static_assert(sumOfFirstSquares(0x1FFFFF).second == 0x2AAAA8AAAAB00000ul, ""); -static_assert(sumOfFirstSquares(0x200000).first == false, ""); +static_assert(!sumOfFirstSquares(0x200000).first, ""); static_assert(sumOfFirstSquares(0x200000).second == std::numeric_limits::max(), ""); } // namespace detail @@ -257,7 +257,6 @@ TxQ::MaybeTx::MaybeTx( , account(txn_->getAccountID(sfAccount)) , lastValid(getLastLedgerSequence(*txn_)) , seqProxy(txn_->getSeqProxy()) - , retriesRemaining(retriesAllowed) , flags(flags_) , pfResult(pfResult_) { @@ -268,7 +267,7 @@ 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 stNumberSO{view.rules().enabled(fixUniversalNumber)}; + NumberSO const stNumberSO{view.rules().enabled(fixUniversalNumber)}; if (pfResult->rules != view.rules() || pfResult->flags != flags) { @@ -308,10 +307,11 @@ TxQ::MaybeTx& TxQ::TxQAccount::add(MaybeTx&& txn) { auto const seqProx = txn.seqProxy; + [[maybe_unused]] auto const* txnPtr = &txn; auto result = transactions.emplace(seqProx, std::move(txn)); XRPL_ASSERT(result.second, "xrpl::TxQ::TxQAccount::add : emplace succeeded"); - XRPL_ASSERT(&result.first->second != &txn, "xrpl::TxQ::TxQAccount::add : transaction moved"); + XRPL_ASSERT(&result.first->second != txnPtr, "xrpl::TxQ::TxQAccount::add : transaction moved"); return result.first->second; } @@ -357,7 +357,7 @@ TxQ::canBeHeld( // queue yet, but should be added in the future. // tapFAIL_HARD transactions are never held if (tx.isFieldPresent(sfPreviousTxnID) || tx.isFieldPresent(sfAccountTxnID) || - (flags & tapFAIL_HARD)) + ((flags & tapFAIL_HARD) != 0u)) return telCAN_NOT_QUEUE; { @@ -386,23 +386,29 @@ TxQ::canBeHeld( // transaction fills the _first_ sequence hole for the account. auto const txSeqProx = tx.getSeqProxy(); if (txSeqProx.isTicket()) + { // Tickets always follow sequence-based transactions, so a ticket // cannot unblock a sequence-based transaction. return telCAN_NOT_QUEUE_FULL; + } // This is the next queuable sequence-based SeqProxy for the account. SeqProxy const nextQueuable = nextQueuableSeqImpl(sleAccount, lock); if (txSeqProx != nextQueuable) + { // The provided transaction does not fill the next open sequence gap. return telCAN_NOT_QUEUE_FULL; + } // Make sure they are not just topping off the account's queued // sequence-based transactions. if (auto const nextTxIter = txQAcct.transactions.upper_bound(nextQueuable); nextTxIter != txQAcct.transactions.end() && nextTxIter->first.isSeq()) + { // There is a next transaction and it is sequence based. They are // filling a real gap. Allow it. return tesSUCCESS; + } return telCAN_NOT_QUEUE_FULL; } @@ -684,13 +690,13 @@ TxQ::apply( ApplyFlags flags, beast::Journal j) { - NumberSO stNumberSO{view.rules().enabled(fixUniversalNumber)}; + 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. auto const pfResult = preflight(app, view.rules(), *tx, flags, j); - if (pfResult.ter != tesSUCCESS) + if (!isTesSuccess(pfResult.ter)) return {pfResult.ter, false}; // See if the transaction paid a high enough fee that it can go straight @@ -718,16 +724,18 @@ TxQ::apply( if (txSeqProx.isTicket() && !view.exists(keylet::ticket(account, txSeqProx))) { if (txSeqProx.value() < acctSeqProx.value()) + { // The ticket number is low enough that it should already be // in the ledger if it were ever going to exist. return {tefNO_TICKET, false}; + } // We don't queue transactions that use Tickets unless // we can find the Ticket in the ledger. return {terPRE_TICKET, false}; } - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); // accountIter is not const because it may be updated further down. AccountMap::iterator accountIter = byAccount_.find(account); @@ -762,9 +770,11 @@ TxQ::apply( TxQAccount::TxMap::iterator const firstIter = acctTxs.lower_bound(acctSeqProx); if (firstIter == acctTxs.end()) + { // Even though there may be transactions in the queue, there are // none that we should pay attention to. return {}; + } return {TxIter{firstIter, acctTxs.end()}}; }(); @@ -928,7 +938,7 @@ TxQ::apply( // // o Additional transactions with Sequences should // follow preceding sequence-based transactions with no - // gaps (except for those required by CreateTicket + // gaps (except for those required by TicketCreate // transactions). // Find the entry in the queue that precedes the new @@ -949,9 +959,13 @@ TxQ::apply( if (txSeqProx.isSeq()) { if (txSeqProx < acctSeqProx) + { return {tefPAST_SEQ, false}; - else if (txSeqProx > acctSeqProx) + } + if (txSeqProx > acctSeqProx) + { return {terPRE_SEQ, false}; + } } } else if (!replacedTxIter) @@ -1272,7 +1286,7 @@ TxQ::apply( void TxQ::processClosedLedger(Application& app, ReadView const& view, bool timeLeap) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); feeMetrics_.update(app, view, timeLeap, setup_); auto const& snapshot = feeMetrics_.getSnapshot(); @@ -1301,9 +1315,13 @@ TxQ::processClosedLedger(Application& app, ReadView const& view, bool timeLeap) for (auto txQAccountIter = byAccount_.begin(); txQAccountIter != byAccount_.end();) { if (txQAccountIter->second.empty()) + { txQAccountIter = byAccount_.erase(txQAccountIter); + } else + { ++txQAccountIter; + } } } @@ -1347,7 +1365,7 @@ TxQ::accept(Application& app, OpenView& view) auto ledgerChanged = false; - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto const metricsSnapshot = feeMetrics_.getSnapshot(); @@ -1393,9 +1411,13 @@ TxQ::accept(Application& app, OpenView& view) candidateIter->retriesRemaining <= 0) { if (candidateIter->retriesRemaining <= 0) + { account.retryPenalty = true; + } else + { account.dropPenalty = true; + } JLOG(j_.debug()) << "Queued transaction " << candidateIter->txID << " failed with " << transToken(txnResult) << ". Remove from queue."; candidateIter = eraseAndAdvance(candidateIter); @@ -1406,9 +1428,13 @@ TxQ::accept(Application& app, OpenView& view) << transToken(txnResult) << ". Leave in queue." << " Applied: " << didApply << ". Flags: " << candidateIter->flags; if (account.retryPenalty && candidateIter->retriesRemaining > 2) + { candidateIter->retriesRemaining = 1; + } else + { --candidateIter->retriesRemaining; + } candidateIter->lastResult = txnResult; if (account.dropPenalty && account.transactions.size() > 1 && isFull<95>()) { @@ -1447,7 +1473,9 @@ TxQ::accept(Application& app, OpenView& view) } } else + { ++candidateIter; + } } } else @@ -1462,9 +1490,13 @@ TxQ::accept(Application& app, OpenView& view) // reordered. LedgerHash const& parentHash = view.header().parentHash; if (parentHash == parentHash_) + { JLOG(j_.warn()) << "Parent ledger hash unchanged from " << parentHash; + } else + { parentHash_ = parentHash; + } [[maybe_unused]] auto const startingSize = byFee_.size(); // byFee_ doesn't "own" the candidate objects inside it, so it's @@ -1497,7 +1529,7 @@ TxQ::accept(Application& app, OpenView& view) SeqProxy TxQ::nextQueuableSeq(std::shared_ptr const& sleAccount) const { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return nextQueuableSeqImpl(sleAccount, lock); } @@ -1532,12 +1564,14 @@ TxQ::nextQueuableSeqImpl( TxQAccount::TxMap::const_iterator txIter = acctTxs.lower_bound(acctSeqProx); if (txIter == acctTxs.end() || !txIter->first.isSeq() || txIter->first != acctSeqProx) + { // Either... // o There are no queued sequence-based transactions equal to or // following acctSeqProx or // o acctSeqProx is not currently in the queue. // So acctSeqProx is as good as it gets. return acctSeqProx; + } // There are sequence-based transactions queued that follow acctSeqProx. // Locate the first opening to put a transaction into. @@ -1557,7 +1591,7 @@ TxQ::getRequiredFeeLevel( OpenView& view, ApplyFlags flags, FeeMetrics::Snapshot const& metricsSnapshot, - std::lock_guard const& lock) const + std::lock_guard const& lock) { return FeeMetrics::scaleFeeLevel(metricsSnapshot, view); } @@ -1586,7 +1620,7 @@ TxQ::tryDirectApply( return {}; FeeLevel64 const requiredFeeLevel = [this, &view, flags]() { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return getRequiredFeeLevel(view, flags, feeMetrics_.getSnapshot(), lock); }(); @@ -1610,9 +1644,9 @@ TxQ::tryDirectApply( { // If the applied transaction replaced a transaction in the // queue then remove the replaced transaction. - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); - AccountMap::iterator accountIter = byAccount_.find(account); + AccountMap::iterator const accountIter = byAccount_.find(account); if (accountIter != byAccount_.end()) { TxQAccount& txQAcct = accountIter->second; @@ -1659,7 +1693,7 @@ TxQ::getMetrics(OpenView const& view) const { Metrics result; - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto const snapshot = feeMetrics_.getSnapshot(); @@ -1680,7 +1714,7 @@ TxQ::getTxRequiredFeeAndSeq(OpenView const& view, std::shared_ptr co { auto const account = (*tx)[sfAccount]; - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto const snapshot = feeMetrics_.getSnapshot(); auto const baseFee = calculateBaseFee(view, *tx); @@ -1702,7 +1736,7 @@ TxQ::getAccountTxs(AccountID const& account) const { std::vector result; - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); AccountMap::const_iterator const accountIter{byAccount_.find(account)}; @@ -1722,7 +1756,7 @@ TxQ::getTxs() const { std::vector result; - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); result.reserve(byFee_.size()); @@ -1735,7 +1769,7 @@ TxQ::getTxs() const Json::Value TxQ::doRPC(Application& app) const { - auto const view = app.openLedger().current(); + auto const view = app.getOpenLedger().current(); if (!view) { BOOST_ASSERT(false); @@ -1798,7 +1832,7 @@ setup_TxQ(Config const& config) 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); - std::uint32_t max; + std::uint32_t max = 0; if (set(max, "maximum_txn_in_ledger", section)) { if (max < setup.minimumTxnInLedger) diff --git a/src/xrpld/app/misc/detail/ValidatorList.cpp b/src/xrpld/app/misc/detail/ValidatorList.cpp index 73db2f8bdb..1951c657b0 100644 --- a/src/xrpld/app/misc/detail/ValidatorList.cpp +++ b/src/xrpld/app/misc/detail/ValidatorList.cpp @@ -109,7 +109,7 @@ ValidatorList::ValidatorList( , j_(j) , quorum_(minimumQuorum.value_or(1)) // Genesis ledger quorum , minimumQuorum_(minimumQuorum) - , listThreshold_(1) + { } @@ -132,12 +132,12 @@ ValidatorList::load( ")?" // end optional comment block ); - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; JLOG(j_.debug()) << "Loading configured trusted validator list publisher keys"; std::size_t count = 0; - for (auto key : publisherKeys) + for (auto const& key : publisherKeys) { JLOG(j_.trace()) << "Processing '" << key << "'"; @@ -158,7 +158,7 @@ ValidatorList::load( status = PublisherStatus::revoked; } - if (publisherLists_.count(id)) + if (publisherLists_.contains(id)) { JLOG(j_.warn()) << "Duplicate validator list publisher key: " << key; continue; @@ -181,7 +181,7 @@ ValidatorList::load( { // Want truncated result when dividing an odd integer listThreshold_ = (publisherLists_.size() < 3) ? 1 // - : publisherLists_.size() / 2 + 1; + : (publisherLists_.size() / 2) + 1; JLOG(j_.debug()) << "Validator list threshold computed as " << listThreshold_; } @@ -340,7 +340,7 @@ ValidatorList::cacheValidatorFile(ValidatorList::lock_guard const& lock, PublicK boost::system::error_code ec; Json::Value value = buildFileData(strHex(pubKey), publisherLists_.at(pubKey), j_); - // rippled should be the only process writing to this file, so + // xrpld should be the only process writing to this file, so // if it ever needs to be read, it is not expected to change externally, so // delay the refresh as long as possible: 24 hours. (See also // `ValidatorSite::missingSite()`) @@ -508,34 +508,29 @@ splitMessageParts( 1); return messages.back().numVLs; } - else + + std::optional smallMsg; + smallMsg.emplace(); + smallMsg->set_version(largeMsg.version()); + smallMsg->set_manifest(largeMsg.manifest()); + + for (std::size_t i = begin; i < end; ++i) { - std::optional smallMsg; - smallMsg.emplace(); - smallMsg->set_version(largeMsg.version()); - smallMsg->set_manifest(largeMsg.manifest()); - - for (std::size_t i = begin; i < end; ++i) - { - *smallMsg->add_blobs() = largeMsg.blobs(i); - } - - if (Message::totalSize(*smallMsg) > maxSize) - { - // free up the message space - smallMsg.reset(); - return splitMessage(messages, largeMsg, maxSize, begin, end); - } - else - { - messages.emplace_back( - std::make_shared(*smallMsg, protocol::mtVALIDATOR_LIST_COLLECTION), - sha512Half(*smallMsg), - smallMsg->blobs_size()); - return messages.back().numVLs; - } + *smallMsg->add_blobs() = largeMsg.blobs(i); } - return 0; + + if (Message::totalSize(*smallMsg) > maxSize) + { + // free up the message space + smallMsg.reset(); + return splitMessage(messages, largeMsg, maxSize, begin, end); + } + + messages.emplace_back( + std::make_shared(*smallMsg, protocol::mtVALIDATOR_LIST_COLLECTION), + sha512Half(*smallMsg), + smallMsg->blobs_size()); + return messages.back().numVLs; } // Build a v1 protocol message using only the current VL @@ -608,14 +603,12 @@ buildValidatorListMessage( // split into smaller messages return splitMessage(messages, msg, maxSize); } - else - { - messages.emplace_back( - std::make_shared(msg, protocol::mtVALIDATOR_LIST_COLLECTION), - sha512Half(msg), - msg.blobs_size()); - return messages.back().numVLs; - } + + messages.emplace_back( + std::make_shared(msg, protocol::mtVALIDATOR_LIST_COLLECTION), + sha512Half(msg), + msg.blobs_size()); + return messages.back().numVLs; } [[nodiscard]] @@ -648,15 +641,17 @@ ValidatorList::buildValidatorListMessages( numVLs = buildValidatorListMessage( messages, peerSequence, rawVersion, rawManifest, blobInfos, maxSize); if (messages.empty()) + { // No message was generated. Create an empty placeholder so we // dont' repeat the work later. messages.emplace_back(); + } } // Don't send it next time. return {maxSequence, numVLs}; } - else if (messageVersion == 1 && peerSequence < currentSeq) + if (messageVersion == 1 && peerSequence < currentSeq) { // Version 1 if (messages.empty()) @@ -668,9 +663,11 @@ ValidatorList::buildValidatorListMessages( currentBlob, maxSize); if (messages.empty()) + { // No message was generated. Create an empty placeholder so we // dont' repeat the work later. messages.emplace_back(); + } } // Don't send it next time. @@ -693,15 +690,20 @@ ValidatorList::sendValidatorList( HashRouter& hashRouter, beast::Journal j) { - std::size_t const messageVersion = - peer.supportsFeature(ProtocolFeature::ValidatorList2Propagation) ? 2 - : peer.supportsFeature(ProtocolFeature::ValidatorListPropagation) ? 1 - : 0; - if (!messageVersion) + std::size_t messageVersion = 0; + if (peer.supportsFeature(ProtocolFeature::ValidatorList2Propagation)) + { + messageVersion = 2; + } + else if (peer.supportsFeature(ProtocolFeature::ValidatorListPropagation)) + { + messageVersion = 1; + } + if (messageVersion == 0u) return; auto const [newPeerSequence, numVLs] = buildValidatorListMessages( messageVersion, peerSequence, maxSequence, rawVersion, rawManifest, blobInfos, messages); - if (newPeerSequence) + if (newPeerSequence != 0u) { XRPL_ASSERT( !messages.empty(), @@ -728,11 +730,13 @@ ValidatorList::sendValidatorList( if (sent) { if (messageVersion > 1) + { JLOG(j.debug()) << "Sent " << messages.size() << " validator list collection(s) containing " << numVLs << " validator list(s) for " << strHex(publisherKey) << " with sequence range " << peerSequence << ", " << newPeerSequence << " to " << peer.fingerprint(); + } else { XRPL_ASSERT( @@ -835,7 +839,7 @@ ValidatorList::broadcastBlobs( // the peer, and foreach provides a const& for (auto& peer : overlay.getActivePeers()) { - if (toSkip->count(peer->id()) == 0) + if (!toSkip->contains(peer->id())) { auto const peerSequence = peer->publisherListSequence(publisherKey).value_or(0); if (peerSequence < maxSequence) @@ -897,7 +901,7 @@ ValidatorList::applyListsAndBroadcast( networkOPs.clearUNLBlocked(); } } - bool broadcast = disposition <= ListDisposition::known_sequence; + bool const broadcast = disposition <= ListDisposition::known_sequence; // this function is only called for PublicKeys which are not specified // in the config file (Note: Keys specified in the local config file are @@ -932,7 +936,7 @@ ValidatorList::applyLists( 1) return PublisherListStats{ListDisposition::unsupported_version}; - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; PublisherListStats result; for (auto const& blobInfo : blobs) @@ -955,13 +959,15 @@ ValidatorList::applyLists( result = std::move(stats); } else + { result.mergeDispositions(stats); + } ///////// } // Clean up the collection, because some of the processing may have made it // inconsistent - if (result.publisherKey && publisherLists_.count(*result.publisherKey)) + if (result.publisherKey && publisherLists_.contains(*result.publisherKey)) { auto& pubCollection = publisherLists_[*result.publisherKey]; auto& remaining = pubCollection.remaining; @@ -1017,9 +1023,13 @@ ValidatorList::updatePublisherList( { // Decrement list count for removed keys if (keyListings_[*iOld] <= 1) + { keyListings_.erase(*iOld); + } else + { --keyListings_[*iOld]; + } ++iOld; } else @@ -1038,7 +1048,7 @@ ValidatorList::updatePublisherList( { auto m = deserializeManifest(base64_decode(valManifest)); - if (!m || !keyListings_.count(m->masterKey)) + if (!m || !keyListings_.contains(m->masterKey)) { JLOG(j_.warn()) << "List for " << strHex(pubKey) << " contained untrusted validator manifest"; @@ -1096,10 +1106,10 @@ ValidatorList::applyList( // LCOV_EXCL_STOP } - PublicKey pubKey = *pubKeyOpt; + PublicKey const pubKey = *pubKeyOpt; if (result > ListDisposition::pending) { - if (publisherLists_.count(pubKey)) + if (publisherLists_.contains(pubKey)) { auto const& pubCollection = publisherLists_[pubKey]; if (pubCollection.maxSequence && @@ -1122,15 +1132,17 @@ ValidatorList::applyList( (result == ListDisposition::accepted || result == ListDisposition::expired); if (accepted) + { pubCollection.status = result == ListDisposition::accepted ? PublisherStatus::available : PublisherStatus::expired; + } pubCollection.rawManifest = globalManifest; if (!pubCollection.maxSequence || sequence > *pubCollection.maxSequence) pubCollection.maxSequence = sequence; Json::Value const& newList = list[jss::validators]; std::vector oldList; - if (accepted && pubCollection.remaining.count(sequence) != 0) + if (accepted && pubCollection.remaining.contains(sequence)) { // We've seen this list before and stored it in "remaining". The // normal expected process is that the processed list would have @@ -1226,7 +1238,7 @@ ValidatorList::loadLists() using namespace boost::filesystem; using namespace boost::system::errc; - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; std::vector sites; sites.reserve(publisherLists_.size()); @@ -1244,7 +1256,7 @@ ValidatorList::loadLists() continue; auto size = file_size(fullPath, ec); - if (!ec && !size) + if (!ec && (size == 0u)) { // Treat an empty file as a missing file, because // nobody else is going to write it. @@ -1282,7 +1294,7 @@ ValidatorList::verify( std::string const& blob, std::string const& signature) { - if (!publisherLists_.count(manifest.masterKey)) + if (!publisherLists_.contains(manifest.masterKey)) return {ListDisposition::untrusted, {}}; PublicKey masterPubKey = manifest.masterKey; @@ -1324,14 +1336,23 @@ ValidatorList::verify( auto const now = timeKeeper_.now(); auto const& listCollection = publisherLists_[masterPubKey]; if (validUntil <= validFrom) + { return {ListDisposition::invalid, masterPubKey}; - else if (sequence < listCollection.current.sequence) + } + if (sequence < listCollection.current.sequence) + { return {ListDisposition::stale, masterPubKey}; - else if (sequence == listCollection.current.sequence) + } + if (sequence == listCollection.current.sequence) + { return {ListDisposition::same_sequence, masterPubKey}; - else if (validUntil <= now) + } + if (validUntil <= now) + { return {ListDisposition::expired, masterPubKey}; - else if (validFrom > now) + } + if (validFrom > now) + { // Not yet valid. Return pending if one of the following is true // * There's no maxSequence, indicating this is the first blob seen // for this publisher @@ -1343,10 +1364,11 @@ ValidatorList::verify( // prevents the risk of missing valid data. Else return // known_sequence return !listCollection.maxSequence || sequence > *listCollection.maxSequence || - (listCollection.remaining.count(sequence) == 0 && + (!listCollection.remaining.contains(sequence) && validFrom < listCollection.remaining.at(*listCollection.maxSequence).validFrom) ? std::make_pair(ListDisposition::pending, masterPubKey) : std::make_pair(ListDisposition::known_sequence, masterPubKey); + } } else { @@ -1359,33 +1381,33 @@ ValidatorList::verify( bool ValidatorList::listed(PublicKey const& identity) const { - std::shared_lock read_lock{mutex_}; + std::shared_lock const read_lock{mutex_}; auto const pubKey = validatorManifests_.getMasterKey(identity); - return keyListings_.find(pubKey) != keyListings_.end(); + return keyListings_.contains(pubKey); } bool ValidatorList::trusted(ValidatorList::shared_lock const&, PublicKey const& identity) const { auto const pubKey = validatorManifests_.getMasterKey(identity); - return trustedMasterKeys_.find(pubKey) != trustedMasterKeys_.end(); + return trustedMasterKeys_.contains(pubKey); } bool ValidatorList::trusted(PublicKey const& identity) const { - std::shared_lock read_lock{mutex_}; + std::shared_lock const read_lock{mutex_}; return trusted(read_lock, identity); } std::optional ValidatorList::getListedKey(PublicKey const& identity) const { - std::shared_lock read_lock{mutex_}; + std::shared_lock const read_lock{mutex_}; - auto const pubKey = validatorManifests_.getMasterKey(identity); - if (keyListings_.find(pubKey) != keyListings_.end()) + auto pubKey = validatorManifests_.getMasterKey(identity); + if (keyListings_.contains(pubKey)) return pubKey; return std::nullopt; } @@ -1393,8 +1415,8 @@ ValidatorList::getListedKey(PublicKey const& identity) const std::optional ValidatorList::getTrustedKey(ValidatorList::shared_lock const&, PublicKey const& identity) const { - auto const pubKey = validatorManifests_.getMasterKey(identity); - if (trustedMasterKeys_.find(pubKey) != trustedMasterKeys_.end()) + auto pubKey = validatorManifests_.getMasterKey(identity); + if (trustedMasterKeys_.contains(pubKey)) return pubKey; return std::nullopt; } @@ -1402,7 +1424,7 @@ ValidatorList::getTrustedKey(ValidatorList::shared_lock const&, PublicKey const& std::optional ValidatorList::getTrustedKey(PublicKey const& identity) const { - std::shared_lock read_lock{mutex_}; + std::shared_lock const read_lock{mutex_}; return getTrustedKey(read_lock, identity); } @@ -1410,15 +1432,15 @@ ValidatorList::getTrustedKey(PublicKey const& identity) const bool ValidatorList::trustedPublisher(PublicKey const& identity) const { - std::shared_lock read_lock{mutex_}; - return identity.size() && publisherLists_.count(identity) && + std::shared_lock const read_lock{mutex_}; + return (identity.size() != 0u) && publisherLists_.contains(identity) && publisherLists_.at(identity).status < PublisherStatus::revoked; } std::optional ValidatorList::localPublicKey() const { - std::shared_lock read_lock{mutex_}; + std::shared_lock const read_lock{mutex_}; return localPubKey_; } @@ -1444,9 +1466,13 @@ ValidatorList::removePublisherList( continue; if (iVal->second <= 1) + { keyListings_.erase(iVal); + } else + { --iVal->second; + } } iList->second.current.list.clear(); @@ -1458,13 +1484,13 @@ ValidatorList::removePublisherList( std::size_t ValidatorList::count(ValidatorList::shared_lock const&) const { - return publisherLists_.size() + (localPublisherList.list.size() > 0); + return publisherLists_.size() + static_cast(!localPublisherList.list.empty()); } std::size_t ValidatorList::count() const { - std::shared_lock read_lock{mutex_}; + std::shared_lock const read_lock{mutex_}; return count(read_lock); } @@ -1489,9 +1515,13 @@ ValidatorList::expires(ValidatorList::shared_lock const&) const { (void)sequence; if (check.validFrom <= chainedExpiration) + { chainedExpiration = check.validUntil; + } else + { break; + } } // Earliest @@ -1501,9 +1531,9 @@ ValidatorList::expires(ValidatorList::shared_lock const&) const } } - if (localPublisherList.list.size() > 0) + if (!localPublisherList.list.empty()) { - PublisherList collection = localPublisherList; + PublisherList const collection = localPublisherList; // Unfetched auto const& current = collection; auto chainedExpiration = current.validUntil; @@ -1520,7 +1550,7 @@ ValidatorList::expires(ValidatorList::shared_lock const&) const std::optional ValidatorList::expires() const { - std::shared_lock read_lock{mutex_}; + std::shared_lock const read_lock{mutex_}; return expires(read_lock); } @@ -1529,7 +1559,7 @@ ValidatorList::getJson() const { Json::Value res(Json::objectValue); - std::shared_lock read_lock{mutex_}; + std::shared_lock const read_lock{mutex_}; res[jss::validation_quorum] = static_cast(quorum_); @@ -1550,9 +1580,13 @@ ValidatorList::getJson() const x[jss::expiration] = to_string(*when); if (*when > timeKeeper_.now()) + { x[jss::status] = "active"; + } else + { x[jss::status] = "expired"; + } } } else @@ -1615,7 +1649,7 @@ ValidatorList::getJson() const future.validFrom > timeKeeper_.now() + 600s, "xrpl::ValidatorList::getJson : minimum valid from"); } - if (remaining.size()) + if (remaining.size() != 0u) curr[jss::remaining] = std::move(remaining); } @@ -1653,7 +1687,7 @@ ValidatorList::getJson() const void ValidatorList::for_each_listed(std::function func) const { - std::shared_lock read_lock{mutex_}; + std::shared_lock const read_lock{mutex_}; for (auto const& v : keyListings_) func(v.first, trusted(read_lock, v.first)); @@ -1669,14 +1703,14 @@ ValidatorList::for_each_available( std::size_t maxSequence, uint256 const& hash)> func) const { - std::shared_lock read_lock{mutex_}; + std::shared_lock const read_lock{mutex_}; for (auto const& [key, plCollection] : publisherLists_) { if (plCollection.status != PublisherStatus::available) continue; XRPL_ASSERT( - plCollection.maxSequence != 0, + plCollection.maxSequence.value_or(0) != 0, "xrpl::ValidatorList::for_each_available : nonzero maxSequence"); func( plCollection.rawManifest, @@ -1693,7 +1727,7 @@ ValidatorList::getAvailable( std::string_view pubKey, std::optional forceVersion /* = {} */) { - std::shared_lock read_lock{mutex_}; + std::shared_lock const read_lock{mutex_}; auto const keyBlob = strViewUnHex(pubKey); @@ -1817,7 +1851,7 @@ ValidatorList::updateTrusted( if (timeKeeper_.now() > closeTime + 30s) closeTime = timeKeeper_.now(); - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; // Rotate pending and remove expired published lists bool good = true; @@ -1951,7 +1985,7 @@ ValidatorList::updateTrusted( { for (auto const& k : trustedMasterKeys_) { - if (negativeUNL_.count(k)) + if (negativeUNL_.contains(k)) --effectiveUnlSize; } hash_set negUnlNodeIDs; @@ -1961,7 +1995,7 @@ ValidatorList::updateTrusted( } for (auto const& nid : seenValidators) { - if (negUnlNodeIDs.count(nid)) + if (negUnlNodeIDs.contains(nid)) --seenSize; } } @@ -1977,7 +2011,7 @@ ValidatorList::updateTrusted( << " exceeds the number of trusted validators (" << unlSize << ")"; } - if ((publisherLists_.size() || localPublisherList.list.size()) && unlSize == 0) + if ((!publisherLists_.empty() || !localPublisherList.list.empty()) && unlSize == 0) { // No validators. Lock down. ops.setUNLBlocked(); @@ -1989,28 +2023,28 @@ ValidatorList::updateTrusted( hash_set ValidatorList::getTrustedMasterKeys() const { - std::shared_lock read_lock{mutex_}; + std::shared_lock const read_lock{mutex_}; return trustedMasterKeys_; } std::size_t ValidatorList::getListThreshold() const { - std::shared_lock read_lock{mutex_}; + std::shared_lock const read_lock{mutex_}; return listThreshold_; } hash_set ValidatorList::getNegativeUNL() const { - std::shared_lock read_lock{mutex_}; + std::shared_lock const read_lock{mutex_}; return negativeUNL_; } void ValidatorList::setNegativeUNL(hash_set const& negUnl) { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; negativeUNL_ = negUnl; } @@ -2031,12 +2065,10 @@ ValidatorList::negativeUNLFilter(std::vector>&& va if (auto const masterKey = getTrustedKey(read_lock, v->getSignerPublic()); masterKey) { - return negativeUNL_.count(*masterKey); - } - else - { - return false; + return negativeUNL_.contains(*masterKey); } + + return false; }), ret.end()); } diff --git a/src/xrpld/app/misc/detail/ValidatorSite.cpp b/src/xrpld/app/misc/detail/ValidatorSite.cpp index c4077a1b8b..a4623e7acc 100644 --- a/src/xrpld/app/misc/detail/ValidatorSite.cpp +++ b/src/xrpld/app/misc/detail/ValidatorSite.cpp @@ -52,17 +52,17 @@ ValidatorSite::Site::Resource::Resource(std::string uri_) : uri{std::move(uri_)} pUrl.port = 443; } else + { throw std::runtime_error("Unsupported scheme: '" + pUrl.scheme + "'"); + } } ValidatorSite::Site::Site(std::string uri) : loadedResource{std::make_shared(std::move(uri))} , startingResource{loadedResource} - , redirCount{0} , refreshInterval{default_refresh_interval} , nextRefresh{clock_type::now()} - , lastRequestEndpoint{} - , lastRequestSuccessful{false} + { } @@ -71,7 +71,7 @@ ValidatorSite::ValidatorSite( std::optional j, std::chrono::seconds timeout) : app_{app} - , j_{j ? *j : app_.logs().journal("ValidatorSite")} + , j_{j ? *j : app_.getJournal("ValidatorSite")} , timer_{app_.getIOContext()} , fetching_{false} , pending_{false} @@ -100,7 +100,7 @@ ValidatorSite::~ValidatorSite() bool ValidatorSite::missingSite(std::lock_guard const& lock_sites) { - auto const sites = app_.validators().loadLists(); + auto const sites = app_.getValidators().loadLists(); return sites.empty() || load(sites, lock_sites); } @@ -109,7 +109,7 @@ ValidatorSite::load(std::vector const& siteURIs) { JLOG(j_.debug()) << "Loading configured validator list sites"; - std::lock_guard lock{sites_mutex_}; + std::lock_guard const lock{sites_mutex_}; return load(siteURIs, lock); } @@ -146,8 +146,8 @@ ValidatorSite::load( void ValidatorSite::start() { - std::lock_guard l0{sites_mutex_}; - std::lock_guard l1{state_mutex_}; + std::lock_guard const l0{sites_mutex_}; + std::lock_guard const l1{state_mutex_}; if (timer_.expiry() == clock_type::time_point{}) setTimer(l0, l1); } @@ -215,7 +215,7 @@ ValidatorSite::makeRequest( sites_[siteIdx].activeResource = resource; std::shared_ptr sp; auto timeoutCancel = [this]() { - std::lock_guard lock_state{state_mutex_}; + std::lock_guard const lock_state{state_mutex_}; // docs indicate cancel_one() can throw, but this // should be reconsidered if it changes to noexcept try @@ -226,12 +226,13 @@ ValidatorSite::makeRequest( { } }; - auto onFetch = - [this, siteIdx, timeoutCancel]( - error_code const& err, endpoint_type const& endpoint, detail::response_type&& resp) { - timeoutCancel(); - onSiteFetch(err, endpoint, std::move(resp), siteIdx); - }; + auto onFetch = [this, siteIdx, timeoutCancel]( + error_code const& err, + endpoint_type const& endpoint, + detail::response_type const& resp) { + timeoutCancel(); + onSiteFetch(err, endpoint, resp, siteIdx); + }; auto onFetchFile = [this, siteIdx, timeoutCancel]( error_code const& err, std::string const& resp) { @@ -278,7 +279,7 @@ ValidatorSite::makeRequest( sp->run(); // start a timer for the request, which shouldn't take more // than requestTimeout_ to complete - std::lock_guard lock_state{state_mutex_}; + std::lock_guard const lock_state{state_mutex_}; timer_.expires_after(requestTimeout_); timer_.async_wait([this, siteIdx](boost::system::error_code const& ec) { this->onRequestTimeout(siteIdx, ec); @@ -292,7 +293,7 @@ ValidatorSite::onRequestTimeout(std::size_t siteIdx, error_code const& ec) return; { - std::lock_guard lock_site{sites_mutex_}; + std::lock_guard const lock_site{sites_mutex_}; // 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 @@ -301,13 +302,15 @@ ValidatorSite::onRequestTimeout(std::size_t siteIdx, error_code const& ec) // first, which will leave activeResource empty. auto const& site = sites_[siteIdx]; if (site.activeResource) + { JLOG(j_.warn()) << "Request for " << site.activeResource->uri << " took too long"; + } else JLOG(j_.error()) << "Request took too long, but a response has " "already been processed"; } - std::lock_guard lock_state{state_mutex_}; + std::lock_guard const lock_state{state_mutex_}; if (auto sp = work_.lock()) sp->cancel(); } @@ -326,7 +329,7 @@ ValidatorSite::onTimer(std::size_t siteIdx, error_code const& ec) try { - std::lock_guard lock{sites_mutex_}; + std::lock_guard const lock{sites_mutex_}; sites_[siteIdx].nextRefresh = clock_type::now() + sites_[siteIdx].refreshInterval; sites_[siteIdx].redirCount = 0; // the WorkSSL client ctor can throw if SSL init fails @@ -367,7 +370,7 @@ ValidatorSite::parseJsonResponse( body[jss::manifest].isString() && body.isMember(jss::version) && body[jss::version].isInt(); // Check the version-specific blob & signature fields - std::uint32_t version; + std::uint32_t version = 0; std::vector blobs; if (valid) { @@ -391,8 +394,15 @@ ValidatorSite::parseJsonResponse( "xrpl::ValidatorSite::parseJsonResponse : version match"); auto const& uri = sites_[siteIdx].activeResource->uri; auto const hash = sha512Half(manifest, blobs, version); - auto const applyResult = app_.validators().applyListsAndBroadcast( - manifest, version, blobs, uri, hash, app_.overlay(), app_.getHashRouter(), app_.getOPs()); + auto const applyResult = app_.getValidators().applyListsAndBroadcast( + manifest, + version, + blobs, + uri, + hash, + app_.getOverlay(), + app_.getHashRouter(), + app_.getOPs()); sites_[siteIdx].lastRefreshStatus.emplace( Site::Status{clock_type::now(), applyResult.bestDisposition(), ""}); @@ -453,13 +463,13 @@ ValidatorSite::parseJsonResponse( std::shared_ptr ValidatorSite::processRedirect( - detail::response_type& res, + detail::response_type const& res, std::size_t siteIdx, std::lock_guard const& sites_lock) { using namespace boost::beast::http; std::shared_ptr newLocation; - if (res.find(field::location) == res.end() || res[field::location].empty()) + if (!res.contains(field::location) || res[field::location].empty()) { JLOG(j_.warn()) << "Request for validator list at " << sites_[siteIdx].activeResource->uri << " returned a redirect with no Location."; @@ -496,7 +506,7 @@ void ValidatorSite::onSiteFetch( boost::system::error_code const& ec, endpoint_type const& endpoint, - detail::response_type&& res, + detail::response_type const& res, std::size_t siteIdx) { std::lock_guard lock_sites{sites_mutex_}; @@ -568,7 +578,7 @@ ValidatorSite::onSiteFetch( sites_[siteIdx].activeResource.reset(); } - std::lock_guard lock_state{state_mutex_}; + std::lock_guard const lock_state{state_mutex_}; fetching_ = false; if (!stopping_) setTimer(lock_sites, lock_state); @@ -581,7 +591,7 @@ ValidatorSite::onTextFetch( std::string const& res, std::size_t siteIdx) { - std::lock_guard lock_sites{sites_mutex_}; + std::lock_guard const lock_sites{sites_mutex_}; { try { @@ -605,7 +615,7 @@ ValidatorSite::onTextFetch( sites_[siteIdx].activeResource.reset(); } - std::lock_guard lock_state{state_mutex_}; + std::lock_guard const lock_state{state_mutex_}; fetching_ = false; if (!stopping_) setTimer(lock_sites, lock_state); @@ -621,7 +631,7 @@ ValidatorSite::getJson() const Json::Value jrr(Json::objectValue); Json::Value& jSites = (jrr[jss::validator_sites] = Json::arrayValue); { - std::lock_guard lock{sites_mutex_}; + std::lock_guard const lock{sites_mutex_}; for (Site const& site : sites_) { Json::Value& v = jSites.append(Json::objectValue); diff --git a/src/xrpld/app/misc/detail/WorkFile.h b/src/xrpld/app/misc/detail/WorkFile.h index 896d7ddc71..5113ec5f3a 100644 --- a/src/xrpld/app/misc/detail/WorkFile.h +++ b/src/xrpld/app/misc/detail/WorkFile.h @@ -44,18 +44,18 @@ private: //------------------------------------------------------------------------------ -WorkFile::WorkFile(std::string const& path, boost::asio::io_context& ios, callback_type cb) +inline WorkFile::WorkFile(std::string const& path, boost::asio::io_context& ios, callback_type cb) : path_(path), cb_(std::move(cb)), ios_(ios), strand_(boost::asio::make_strand(ios)) { } -WorkFile::~WorkFile() +inline WorkFile::~WorkFile() { if (cb_) cb_(make_error_code(boost::system::errc::interrupted), {}); } -void +inline void WorkFile::run() { if (!strand_.running_in_this_thread()) @@ -71,7 +71,7 @@ WorkFile::run() cb_ = nullptr; } -void +inline void WorkFile::cancel() { // Nothing to do. Either it finished in run, or it didn't start. diff --git a/src/xrpld/app/misc/detail/WorkPlain.h b/src/xrpld/app/misc/detail/WorkPlain.h index d1df5b4b3b..361a7b4513 100644 --- a/src/xrpld/app/misc/detail/WorkPlain.h +++ b/src/xrpld/app/misc/detail/WorkPlain.h @@ -35,7 +35,7 @@ private: //------------------------------------------------------------------------------ -WorkPlain::WorkPlain( +inline WorkPlain::WorkPlain( std::string const& host, std::string const& path, std::string const& port, @@ -47,7 +47,7 @@ WorkPlain::WorkPlain( { } -void +inline void WorkPlain::onConnect(error_code const& ec) { if (ec) diff --git a/src/xrpld/app/misc/detail/WorkSSL.cpp b/src/xrpld/app/misc/detail/WorkSSL.cpp index 47902e900a..3ae0db1a96 100644 --- a/src/xrpld/app/misc/detail/WorkSSL.cpp +++ b/src/xrpld/app/misc/detail/WorkSSL.cpp @@ -32,7 +32,10 @@ WorkSSL::onConnect(error_code const& ec) { auto err = ec ? ec : context_.postConnectVerify(stream_, host_); if (err) - return fail(err); + { + fail(err); + return; + } stream_.async_handshake( boost::asio::ssl::stream_base::client, @@ -44,7 +47,10 @@ void WorkSSL::onHandshake(error_code const& ec) { if (ec) - return fail(ec); + { + fail(ec); + return; + } onStart(); } diff --git a/src/xrpld/app/misc/detail/setup_HashRouter.cpp b/src/xrpld/app/misc/detail/setup_HashRouter.cpp index a0e63dd67e..0cc61f0730 100644 --- a/src/xrpld/app/misc/detail/setup_HashRouter.cpp +++ b/src/xrpld/app/misc/detail/setup_HashRouter.cpp @@ -18,17 +18,21 @@ setup_HashRouter(Config const& config) if (set(tmp, "hold_time", section)) { if (tmp < 12) + { Throw( "HashRouter hold time must be at least 12 seconds (the " "approximate validation time for three ledgers)."); + } setup.holdTime = seconds(tmp); } if (set(tmp, "relay_time", section)) { if (tmp < 8) + { Throw( "HashRouter relay time must be at least 8 seconds (the " "approximate validation time for two ledgers)."); + } setup.relayTime = seconds(tmp); } if (setup.relayTime > setup.holdTime) diff --git a/src/xrpld/app/misc/make_NetworkOPs.h b/src/xrpld/app/misc/make_NetworkOPs.h index 7dce966f04..e250a147b7 100644 --- a/src/xrpld/app/misc/make_NetworkOPs.h +++ b/src/xrpld/app/misc/make_NetworkOPs.h @@ -21,11 +21,11 @@ make_NetworkOPs( NetworkOPs::clock_type& clock, bool standalone, std::size_t minPeerCount, - bool start_valid, - JobQueue& job_queue, + bool startValid, + JobQueue& jobQueue, LedgerMaster& ledgerMaster, ValidatorKeys const& validatorKeys, - boost::asio::io_context& io_svc, + boost::asio::io_context& ioCtx, beast::Journal journal, beast::insight::Collector::ptr const& collector); diff --git a/src/xrpld/app/rdb/README.md b/src/xrpld/app/rdb/README.md index a50bb395c1..53e6b0c6fa 100644 --- a/src/xrpld/app/rdb/README.md +++ b/src/xrpld/app/rdb/README.md @@ -2,7 +2,7 @@ The guiding principles of the Relational Database Interface are summarized below: -- All hard-coded SQL statements should be stored in the [files](#source-files) under the `xrpld/app/rdb` directory. With the exception of test modules, no hard-coded SQL should be added to any other file in rippled. +- All hard-coded SQL statements should be stored in the [files](#source-files) under the `xrpld/app/rdb` directory. With the exception of test modules, no hard-coded SQL should be added to any other file in xrpld. - The base class `RelationalDatabase` is inherited by derived classes that each provide an interface for operating on distinct relational database systems. ## Overview diff --git a/src/xrpld/app/rdb/backend/SQLiteDatabase.h b/src/xrpld/app/rdb/backend/SQLiteDatabase.h index 462b62de6e..05e0175732 100644 --- a/src/xrpld/app/rdb/backend/SQLiteDatabase.h +++ b/src/xrpld/app/rdb/backend/SQLiteDatabase.h @@ -327,10 +327,10 @@ public: * @param id Hash of the transaction. * @param range Range of ledgers to check, if present. * @param ec Default error code value. - * @return Transaction and its metadata if found, otherwise TxSearched::all + * @return Transaction and its metadata if found, otherwise TxSearched::All * if a range is provided and all ledgers from the range are present - * in the database, TxSearched::some if a range is provided and not - * all ledgers are present, TxSearched::unknown if the range is not + * in the database, TxSearched::Some if a range is provided and not + * all ledgers are present, TxSearched::Unknown if the range is not * provided or a deserializing error occurred. In the last case the * error code is returned via the ec parameter, in other cases the * default error code is not changed. @@ -405,7 +405,7 @@ public: transactionDbHasSpace(Config const& config); private: - ServiceRegistry& registry_; + std::reference_wrapper registry_; bool useTxTables_; beast::Journal j_; std::unique_ptr ledgerDb_, txdb_; diff --git a/src/xrpld/app/rdb/backend/detail/Node.cpp b/src/xrpld/app/rdb/backend/detail/Node.cpp index 9425cd9159..bc19a25b40 100644 --- a/src/xrpld/app/rdb/backend/detail/Node.cpp +++ b/src/xrpld/app/rdb/backend/detail/Node.cpp @@ -1,7 +1,7 @@ #include #include +#include #include -#include #include #include @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -69,13 +70,13 @@ makeLedgerDBs( boost::format("PRAGMA cache_size=-%d;") % kilobytes(config.getValueFor(SizedItem::txnDBCache))); - if (!setup.standAlone || setup.startUp == StartUpType::LOAD || - setup.startUp == StartUpType::LOAD_FILE || setup.startUp == StartUpType::REPLAY) + if (!setup.standAlone || setup.startUp == StartUpType::Load || + setup.startUp == StartUpType::LoadFile || setup.startUp == StartUpType::Replay) { // Check if AccountTransactions has primary key std::string cid, name, type; - std::size_t notnull, dflt_value, pk; - soci::indicator ind; + std::size_t notnull = 0, dflt_value = 0, pk = 0; + soci::indicator ind = soci::i_null; soci::statement st = (tx->getSession().prepare << ("PRAGMA table_info(AccountTransactions);"), soci::into(cid), @@ -97,14 +98,14 @@ makeLedgerDBs( return {std::move(lgr), std::move(tx), true}; } - else - return {std::move(lgr), {}, true}; + + return {std::move(lgr), {}, true}; } std::optional getMinLedgerSeq(soci::session& session, TableType type) { - std::string query = "SELECT MIN(LedgerSeq) FROM " + to_string(type) + ";"; + std::string const query = "SELECT MIN(LedgerSeq) FROM " + to_string(type) + ";"; // SOCI requires boost::optional (not std::optional) as the parameter. boost::optional m; session << query, soci::into(m); @@ -114,7 +115,7 @@ getMinLedgerSeq(soci::session& session, TableType type) std::optional getMaxLedgerSeq(soci::session& session, TableType type) { - std::string query = "SELECT MAX(LedgerSeq) FROM " + to_string(type) + ";"; + std::string const query = "SELECT MAX(LedgerSeq) FROM " + to_string(type) + ";"; // SOCI requires boost::optional (not std::optional) as the parameter. boost::optional m; session << query, soci::into(m); @@ -136,7 +137,7 @@ deleteBeforeLedgerSeq(soci::session& session, TableType type, LedgerIndex ledger std::size_t getRows(soci::session& session, TableType type) { - std::size_t rows; + std::size_t rows = 0; session << "SELECT COUNT(*) AS rows " "FROM " << to_string(type) << ";", @@ -148,7 +149,7 @@ getRows(soci::session& session, TableType type) RelationalDatabase::CountMinMax getRowsMinMax(soci::session& session, TableType type) { - RelationalDatabase::CountMinMax res; + RelationalDatabase::CountMinMax res{}; session << "SELECT COUNT(*) AS rows, " "MIN(LedgerSeq) AS first, " "MAX(LedgerSeq) AS last " @@ -168,7 +169,7 @@ saveValidatedLedger( std::shared_ptr const& ledger, bool current) { - auto j = app.journal("Ledger"); + auto j = app.getJournal("Ledger"); auto seq = ledger->header().seq; // TODO(tom): Fix this hard-coded SQL! @@ -221,7 +222,7 @@ saveValidatedLedger( app.getLedgerMaster().failedSave(seq, ledger->header().hash); // Clients can now trust the database for information about this // ledger sequence. - app.pendingSaves().finishWork(seq); + app.getPendingSaves().finishWork(seq); return false; } @@ -282,7 +283,9 @@ saveValidatedLedger( for (auto const& account : accts) { if (!first) + { sql += ", ('"; + } else { sql += "('"; @@ -327,7 +330,7 @@ saveValidatedLedger( } { - static std::string addLedger( + static std::string const addLedger( R"sql(INSERT OR REPLACE INTO Ledgers (LedgerHash,LedgerSeq,PrevHash,TotalCoins,ClosingTime,PrevClosingTime, CloseTimeRes,CloseFlags,AccountSetHash,TransSetHash) @@ -543,7 +546,7 @@ getHashesByIndex(soci::session& session, LedgerIndex minSeq, LedgerIndex maxSeq, sql.append(std::to_string(maxSeq)); sql.append(";"); - std::uint64_t ls; + std::uint64_t ls = 0; std::string lh; // SOCI requires boost::optional (not std::optional) as the parameter. boost::optional ph; @@ -573,7 +576,7 @@ getHashesByIndex(soci::session& session, LedgerIndex minSeq, LedgerIndex maxSeq, std::pair>, int> getTxHistory(soci::session& session, Application& app, LedgerIndex startIndex, int quantity) { - std::string sql = boost::str( + std::string const sql = boost::str( boost::format( "SELECT LedgerSeq, Status, RawTxn " "FROM Transactions ORDER BY LedgerSeq DESC LIMIT %u,%u;") % @@ -587,7 +590,7 @@ getTxHistory(soci::session& session, Application& app, LedgerIndex startIndex, i boost::optional ledgerSeq; boost::optional status; soci::blob sociRawTxnBlob(session); - soci::indicator rti; + soci::indicator rti = soci::i_null; Blob rawTxn; soci::statement st = @@ -600,9 +603,13 @@ getTxHistory(soci::session& session, Application& app, LedgerIndex startIndex, i while (st.fetch()) { if (soci::i_ok == rti) + { convert(sociRawTxnBlob, rawTxn); + } else + { rawTxn.clear(); + } if (auto trans = Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app)) { @@ -645,7 +652,7 @@ transactionsSQL( constexpr std::uint32_t NONBINARY_PAGE_LENGTH = 200; constexpr std::uint32_t BINARY_PAGE_LENGTH = 500; - std::uint32_t numberOfResults; + std::uint32_t numberOfResults = 0; if (count) { @@ -665,31 +672,34 @@ transactionsSQL( numberOfResults = options.limit; } - std::string maxClause = ""; - std::string minClause = ""; + std::string maxClause; + std::string minClause; - if (options.maxLedger) + if (options.ledgerRange.max != 0u) { maxClause = boost::str( - boost::format("AND AccountTransactions.LedgerSeq <= '%u'") % options.maxLedger); + boost::format("AND AccountTransactions.LedgerSeq <= '%u'") % options.ledgerRange.max); } - if (options.minLedger) + if (options.ledgerRange.min != 0u) { minClause = boost::str( - boost::format("AND AccountTransactions.LedgerSeq >= '%u'") % options.minLedger); + boost::format("AND AccountTransactions.LedgerSeq >= '%u'") % options.ledgerRange.min); } std::string sql; if (count) + { sql = boost::str( boost::format( "SELECT %s FROM AccountTransactions " "WHERE Account = '%s' %s %s LIMIT %u, %u;") % selection % toBase58(options.account) % maxClause % minClause % options.offset % numberOfResults); + } else + { sql = boost::str( boost::format( "SELECT %s FROM " @@ -702,6 +712,7 @@ transactionsSQL( selection % toBase58(options.account) % maxClause % minClause % (descending ? "DESC" : "ASC") % (descending ? "DESC" : "ASC") % (descending ? "DESC" : "ASC") % options.offset % numberOfResults); + } JLOG(j.trace()) << "txSQL query: " << sql; return sql; } @@ -738,7 +749,7 @@ getAccountTxs( { RelationalDatabase::AccountTxs ret; - std::string sql = transactionsSQL( + std::string const sql = transactionsSQL( app, "AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta", options, @@ -746,7 +757,7 @@ getAccountTxs( false, false, j); - if (sql == "") + if (sql.empty()) return {ret, 0}; int total = 0; @@ -755,7 +766,7 @@ getAccountTxs( boost::optional ledgerSeq; boost::optional status; soci::blob sociTxnBlob(session), sociTxnMetaBlob(session); - soci::indicator rti, tmi; + soci::indicator rti = soci::i_null, tmi = soci::i_null; Blob rawTxn, txnMeta; soci::statement st = @@ -769,14 +780,22 @@ getAccountTxs( while (st.fetch()) { if (soci::i_ok == rti) + { convert(sociTxnBlob, rawTxn); + } else + { rawTxn.clear(); + } if (soci::i_ok == tmi) + { convert(sociTxnMetaBlob, txnMeta); + } else + { txnMeta.clear(); + } auto txn = Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app); @@ -854,7 +873,7 @@ getAccountTxsB( { std::vector ret; - std::string sql = transactionsSQL( + std::string const sql = transactionsSQL( app, "AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta", options, @@ -862,7 +881,7 @@ getAccountTxsB( true /*binary*/, false, j); - if (sql == "") + if (sql.empty()) return {ret, 0}; int total = 0; @@ -872,7 +891,7 @@ getAccountTxsB( boost::optional ledgerSeq; boost::optional status; soci::blob sociTxnBlob(session), sociTxnMetaBlob(session); - soci::indicator rti, tmi; + soci::indicator rti = soci::i_null, tmi = soci::i_null; soci::statement st = (session.prepare << sql, @@ -953,13 +972,17 @@ accountTxPage( bool lookingForMarker = options.marker.has_value(); - std::uint32_t numberOfResults; + std::uint32_t numberOfResults = 0; if (options.limit == 0 || options.limit == UINT32_MAX || (options.limit > page_length && !options.bAdmin)) + { numberOfResults = page_length; + } else + { numberOfResults = options.limit; + } // As an account can have many thousands of transactions, there is a limit // placed on the amount of transactions returned. If the limit is reached @@ -998,14 +1021,14 @@ accountTxPage( ORDER BY AccountTransactions.LedgerSeq %s, AccountTransactions.TxnSeq %s LIMIT %u;)")) % - toBase58(options.account) % options.minLedger % options.maxLedger % order % order % - queryLimit); + toBase58(options.account) % options.ledgerRange.min % options.ledgerRange.max % order % + order % queryLimit); } else { char const* const compare = forward ? ">=" : "<="; - std::uint32_t const minLedger = forward ? findLedger + 1 : options.minLedger; - std::uint32_t const maxLedger = forward ? options.maxLedger : findLedger - 1; + std::uint32_t const minLedger = forward ? findLedger + 1 : options.ledgerRange.min; + std::uint32_t const maxLedger = forward ? options.ledgerRange.max : findLedger - 1; auto b58acct = toBase58(options.account); sql = boost::str( @@ -1041,7 +1064,7 @@ accountTxPage( boost::optional status; soci::blob txnData(session); soci::blob txnMeta(session); - soci::indicator dataPresent, metaPresent; + soci::indicator dataPresent = soci::i_null, metaPresent = soci::i_null; soci::statement st = (session.prepare << sql, @@ -1062,7 +1085,9 @@ accountTxPage( lookingForMarker = false; } else + { continue; + } } else if (numberOfResults == 0) { @@ -1072,17 +1097,25 @@ accountTxPage( } if (dataPresent == soci::i_ok) + { convert(txnData, rawData); + } else + { rawData.clear(); + } if (metaPresent == soci::i_ok) + { convert(txnMeta, rawMeta); + } else + { rawMeta.clear(); + } // Work around a bug that could leave the metadata missing - if (rawMeta.size() == 0) + if (rawMeta.empty()) onUnsavedLedger(ledgerSeq.value_or(0)); // `rawData` and `rawMeta` will be used after they are moved. @@ -1151,7 +1184,7 @@ getTransaction( Blob rawTxn, rawMeta; { soci::blob sociRawTxnBlob(session), sociRawMetaBlob(session); - soci::indicator txn, meta; + soci::indicator txn = soci::i_null, meta = soci::i_null; session << sql, soci::into(ledgerSeq), soci::into(status), soci::into(sociRawTxnBlob, txn), soci::into(sociRawMetaBlob, meta); @@ -1159,12 +1192,12 @@ getTransaction( auto const got_data = session.got_data(); if ((!got_data || txn != soci::i_ok || meta != soci::i_ok) && !range) - return TxSearched::unknown; + return TxSearched::Unknown; if (!got_data) { uint64_t count = 0; - soci::indicator rti; + soci::indicator rti = soci::i_null; session << "SELECT COUNT(DISTINCT LedgerSeq) FROM Transactions WHERE " "LedgerSeq BETWEEN " @@ -1172,10 +1205,10 @@ getTransaction( soci::into(count, rti); if (!session.got_data() || rti != soci::i_ok) - return TxSearched::some; + return TxSearched::Some; - return count == (range->last() - range->first() + 1) ? TxSearched::all - : TxSearched::some; + return count == (range->last() - range->first() + 1) ? TxSearched::All + : TxSearched::Some; } convert(sociRawTxnBlob, rawTxn); @@ -1189,7 +1222,7 @@ getTransaction( if (!ledgerSeq) return std::pair{std::move(txn), nullptr}; - std::uint32_t inLedger = rangeCheckedCast(ledgerSeq.value()); + std::uint32_t const inLedger = rangeCheckedCast(ledgerSeq.value()); auto txMeta = std::make_shared(id, inLedger, rawMeta); @@ -1197,19 +1230,20 @@ getTransaction( } catch (std::exception& e) { - JLOG(app.journal("Ledger").warn()) + JLOG(app.getJournal("Ledger").warn()) << "Unable to deserialize transaction from raw SQL value. Error: " << e.what(); ec = rpcDB_DESERIALIZATION; } - return TxSearched::unknown; + return TxSearched::Unknown; } bool dbHasSpace(soci::session& session, Config const& config, beast::Journal j) { - boost::filesystem::space_info space = boost::filesystem::space(config.legacy("database_path")); + boost::filesystem::space_info const space = + boost::filesystem::space(config.legacy("database_path")); if (space.available < megabytes(512)) { @@ -1219,8 +1253,8 @@ dbHasSpace(soci::session& session, Config const& config, beast::Journal j) if (config.useTxTables()) { - DatabaseCon::Setup dbSetup = setup_DatabaseCon(config); - boost::filesystem::path dbPath = dbSetup.dataDir / TxDBName; + DatabaseCon::Setup const dbSetup = setup_DatabaseCon(config); + boost::filesystem::path const dbPath = dbSetup.dataDir / TxDBName; boost::system::error_code ec; std::optional dbSize = boost::filesystem::file_size(dbPath, ec); if (ec) @@ -1230,19 +1264,19 @@ dbHasSpace(soci::session& session, Config const& config, beast::Journal j) } static auto const pageSize = [&] { - std::uint32_t ps; + std::uint32_t ps = 0; session << "PRAGMA page_size;", soci::into(ps); return ps; }(); static auto const maxPages = [&] { - std::uint32_t mp; + std::uint32_t mp = 0; session << "PRAGMA max_page_count;", soci::into(mp); return mp; }(); - std::uint32_t pageCount; + std::uint32_t pageCount = 0; session << "PRAGMA page_count;", soci::into(pageCount); - std::uint32_t freePages = maxPages - pageCount; - std::uint64_t freeSpace = safe_cast(freePages) * pageSize; + std::uint32_t const freePages = maxPages - pageCount; + std::uint64_t const freeSpace = safe_cast(freePages) * pageSize; JLOG(j.info()) << "Transaction DB pathname: " << dbPath.string() << "; file size: " << dbSize.value_or(-1) << " bytes" << "; SQLite page size: " << pageSize << " bytes" @@ -1254,7 +1288,7 @@ dbHasSpace(soci::session& session, Config const& config, beast::Journal j) if (freeSpace < megabytes(512)) { JLOG(j.fatal()) << "Free SQLite space for transaction db is less than " - "512MB. To fix this, rippled must be executed with the " + "512MB. To fix this, xrpld must be executed with the " "vacuum parameter before restarting. " "Note that this activity can take multiple days, " "depending on database size."; diff --git a/src/xrpld/app/rdb/backend/detail/Node.h b/src/xrpld/app/rdb/backend/detail/Node.h index cb49a373bd..5fbabeca47 100644 --- a/src/xrpld/app/rdb/backend/detail/Node.h +++ b/src/xrpld/app/rdb/backend/detail/Node.h @@ -1,8 +1,8 @@ #pragma once -#include #include +#include #include namespace xrpl { @@ -377,10 +377,10 @@ newestAccountTxPage( * @param id Hash of the transaction. * @param range Range of ledgers to check, if present. * @param ec Default value of error code. - * @return Transaction and its metadata if found, TxSearched::all if range + * @return Transaction and its metadata if found, TxSearched::All if range * given and all ledgers from range are present in the database, - * TxSearched::some if range given and not all ledgers are present, - * TxSearched::unknown if range not given or deserializing error + * TxSearched::Some if range given and not all ledgers are present, + * TxSearched::Unknown if range not given or deserializing error * occurred. In the last case error code modified in ec link * parameter, in other cases default error code remained. */ diff --git a/src/xrpld/app/rdb/backend/detail/SQLiteDatabase.cpp b/src/xrpld/app/rdb/backend/detail/SQLiteDatabase.cpp index 1b15fcf52e..90d3b5a8e4 100644 --- a/src/xrpld/app/rdb/backend/detail/SQLiteDatabase.cpp +++ b/src/xrpld/app/rdb/backend/detail/SQLiteDatabase.cpp @@ -178,7 +178,8 @@ SQLiteDatabase::saveValidatedLedger(std::shared_ptr const& ledger, { if (existsLedger()) { - if (!detail::saveValidatedLedger(*ledgerDb_, txdb_, registry_.app(), ledger, current)) + if (!detail::saveValidatedLedger( + *ledgerDb_, txdb_, registry_.get().getApp(), ledger, current)) return false; } @@ -314,7 +315,7 @@ SQLiteDatabase::getTxHistory(LedgerIndex startIndex) if (existsTransaction()) { auto db = checkoutTransaction(); - auto const res = detail::getTxHistory(*db, registry_.app(), startIndex, 20).first; + auto const res = detail::getTxHistory(*db, registry_.get().getApp(), startIndex, 20).first; if (!res.empty()) return res; @@ -329,12 +330,13 @@ SQLiteDatabase::getOldestAccountTxs(AccountTxOptions const& options) if (!useTxTables_) return {}; - LedgerMaster& ledgerMaster = registry_.getLedgerMaster(); + LedgerMaster& ledgerMaster = registry_.get().getLedgerMaster(); if (existsTransaction()) { auto db = checkoutTransaction(); - return detail::getOldestAccountTxs(*db, registry_.app(), ledgerMaster, options, j_).first; + return detail::getOldestAccountTxs(*db, registry_.get().getApp(), ledgerMaster, options, j_) + .first; } return {}; @@ -346,12 +348,13 @@ SQLiteDatabase::getNewestAccountTxs(AccountTxOptions const& options) if (!useTxTables_) return {}; - LedgerMaster& ledgerMaster = registry_.getLedgerMaster(); + LedgerMaster& ledgerMaster = registry_.get().getLedgerMaster(); if (existsTransaction()) { auto db = checkoutTransaction(); - return detail::getNewestAccountTxs(*db, registry_.app(), ledgerMaster, options, j_).first; + return detail::getNewestAccountTxs(*db, registry_.get().getApp(), ledgerMaster, options, j_) + .first; } return {}; @@ -366,7 +369,7 @@ SQLiteDatabase::getOldestAccountTxsB(AccountTxOptions const& options) if (existsTransaction()) { auto db = checkoutTransaction(); - return detail::getOldestAccountTxsB(*db, registry_.app(), options, j_).first; + return detail::getOldestAccountTxsB(*db, registry_.get().getApp(), options, j_).first; } return {}; @@ -381,7 +384,7 @@ SQLiteDatabase::getNewestAccountTxsB(AccountTxOptions const& options) if (existsTransaction()) { auto db = checkoutTransaction(); - return detail::getNewestAccountTxsB(*db, registry_.app(), options, j_).first; + return detail::getNewestAccountTxsB(*db, registry_.get().getApp(), options, j_).first; } return {}; @@ -395,13 +398,15 @@ SQLiteDatabase::oldestAccountTxPage(AccountTxPageOptions const& options) static std::uint32_t const page_length(200); auto onUnsavedLedger = - std::bind(saveLedgerAsync, std::ref(registry_.app()), std::placeholders::_1); + std::bind(saveLedgerAsync, std::ref(registry_.get().getApp()), std::placeholders::_1); AccountTxs ret; - auto onTransaction = - [&ret, &app = registry_.app()]( - std::uint32_t ledger_index, std::string const& status, Blob&& rawTxn, Blob&& rawMeta) { - convertBlobsToTxResult(ret, ledger_index, status, rawTxn, rawMeta, app); - }; + auto onTransaction = [&ret, &app = registry_.get().getApp()]( + std::uint32_t ledger_index, + std::string const& status, + Blob const& rawTxn, + Blob const& rawMeta) { + convertBlobsToTxResult(ret, ledger_index, status, rawTxn, rawMeta, app); + }; if (existsTransaction()) { @@ -423,13 +428,15 @@ SQLiteDatabase::newestAccountTxPage(AccountTxPageOptions const& options) static std::uint32_t const page_length(200); auto onUnsavedLedger = - std::bind(saveLedgerAsync, std::ref(registry_.app()), std::placeholders::_1); + std::bind(saveLedgerAsync, std::ref(registry_.get().getApp()), std::placeholders::_1); AccountTxs ret; - auto onTransaction = - [&ret, &app = registry_.app()]( - std::uint32_t ledger_index, std::string const& status, Blob&& rawTxn, Blob&& rawMeta) { - convertBlobsToTxResult(ret, ledger_index, status, rawTxn, rawMeta, app); - }; + auto onTransaction = [&ret, &app = registry_.get().getApp()]( + std::uint32_t ledger_index, + std::string const& status, + Blob const& rawTxn, + Blob const& rawMeta) { + convertBlobsToTxResult(ret, ledger_index, status, rawTxn, rawMeta, app); + }; if (existsTransaction()) { @@ -451,7 +458,7 @@ SQLiteDatabase::oldestAccountTxPageB(AccountTxPageOptions const& options) static std::uint32_t const page_length(500); auto onUnsavedLedger = - std::bind(saveLedgerAsync, std::ref(registry_.app()), std::placeholders::_1); + std::bind(saveLedgerAsync, std::ref(registry_.get().getApp()), std::placeholders::_1); MetaTxsList ret; auto onTransaction = [&ret]( @@ -479,7 +486,7 @@ SQLiteDatabase::newestAccountTxPageB(AccountTxPageOptions const& options) static std::uint32_t const page_length(500); auto onUnsavedLedger = - std::bind(saveLedgerAsync, std::ref(registry_.app()), std::placeholders::_1); + std::bind(saveLedgerAsync, std::ref(registry_.get().getApp()), std::placeholders::_1); MetaTxsList ret; auto onTransaction = [&ret]( @@ -506,15 +513,15 @@ SQLiteDatabase::getTransaction( error_code_i& ec) { if (!useTxTables_) - return TxSearched::unknown; + return TxSearched::Unknown; if (existsTransaction()) { auto db = checkoutTransaction(); - return detail::getTransaction(*db, registry_.app(), id, range, ec); + return detail::getTransaction(*db, registry_.get().getApp(), id, range, ec); } - return TxSearched::unknown; + return TxSearched::Unknown; } SQLiteDatabase::SQLiteDatabase(SQLiteDatabase&& rhs) noexcept @@ -602,10 +609,10 @@ SQLiteDatabase::closeTransactionDB() SQLiteDatabase::SQLiteDatabase(ServiceRegistry& registry, Config const& config, JobQueue& jobQueue) : registry_(registry) , useTxTables_(config.useTxTables()) - , j_(registry.journal("SQLiteDatabase")) + , j_(registry.getJournal("SQLiteDatabase")) { DatabaseCon::Setup const setup = setup_DatabaseCon(config, j_); - if (!makeLedgerDBs(config, setup, DatabaseCon::CheckpointerSetup{&jobQueue, ®istry_.logs()})) + if (!makeLedgerDBs(config, setup, DatabaseCon::CheckpointerSetup{&jobQueue, registry_})) { std::string_view constexpr error = "Failed to create ledger databases"; diff --git a/src/xrpld/app/rdb/detail/PeerFinder.cpp b/src/xrpld/app/rdb/detail/PeerFinder.cpp index cfab6f764a..a568461fb7 100644 --- a/src/xrpld/app/rdb/detail/PeerFinder.cpp +++ b/src/xrpld/app/rdb/detail/PeerFinder.cpp @@ -5,7 +5,7 @@ namespace xrpl { void initPeerFinderDB(soci::session& session, BasicConfig const& config, beast::Journal j) { - DBConfig m_sociConfig(config, "peerfinder"); + DBConfig const m_sociConfig(config, "peerfinder"); m_sociConfig.open(session); JLOG(j.info()) << "Opening database at '" << m_sociConfig.connectionString() << "'"; @@ -83,7 +83,7 @@ updatePeerFinderDB(soci::session& session, int currentSchemaVersion, beast::Jour " PeerFinder_BootstrapCache_Next " " ( address ); "; - std::size_t count; + std::size_t count = 0; session << "SELECT COUNT(*) FROM PeerFinder_BootstrapCache;", soci::into(count); std::vector list; @@ -91,7 +91,7 @@ updatePeerFinderDB(soci::session& session, int currentSchemaVersion, beast::Jour { list.reserve(count); std::string s; - int valence; + int valence = 0; soci::statement st = (session.prepare << "SELECT " " address, " @@ -187,7 +187,7 @@ void readPeerFinderDB(soci::session& session, std::function const& func) { std::string s; - int valence; + int valence = 0; soci::statement st = (session.prepare << "SELECT " " address, " diff --git a/src/xrpld/consensus/Consensus.cpp b/src/xrpld/consensus/Consensus.cpp index 29d0e9db1c..9ad7e677ad 100644 --- a/src/xrpld/consensus/Consensus.cpp +++ b/src/xrpld/consensus/Consensus.cpp @@ -132,7 +132,7 @@ checkConsensusReached( CLOG(clog) << "agreeing and total adjusted: " << agreeing << ',' << total << ". "; } - std::size_t currentPercentage = (agreeing * 100) / total; + std::size_t const currentPercentage = (agreeing * 100) / total; CLOG(clog) << "currentPercentage: " << currentPercentage; bool const ret = currentPercentage >= minConsensusPct; diff --git a/src/xrpld/consensus/Consensus.h b/src/xrpld/consensus/Consensus.h index 3c1e4e7dbf..142b1a01f0 100644 --- a/src/xrpld/consensus/Consensus.h +++ b/src/xrpld/consensus/Consensus.h @@ -4,12 +4,12 @@ #include #include #include -#include #include #include #include #include +#include #include #include @@ -554,7 +554,7 @@ private: ConsensusParms::AvalancheState closeTimeAvalancheState_ = ConsensusParms::init; // Time it took for the last consensus round to converge - std::chrono::milliseconds prevRoundTime_; + std::chrono::milliseconds prevRoundTime_{}; //------------------------------------------------------------------------- // Network time measurements of consensus progress @@ -1116,7 +1116,7 @@ Consensus::phaseOpen(std::unique_ptr const& clog) using namespace std::chrono; // it is shortly before ledger close time - bool anyTransactions = adaptor_.hasOpenTransactions(); + bool const anyTransactions = adaptor_.hasOpenTransactions(); auto proposersClosed = currPeerPositions_.size(); auto proposersValidated = adaptor_.proposersValidated(prevLedgerID_); @@ -1186,7 +1186,7 @@ Consensus::shouldPause(std::unique_ptr const& clog) previousLedger_.seq() - std::min(adaptor_.getValidLedgerIndex(), previousLedger_.seq())); auto [quorum, trustedKeys] = adaptor_.getQuorumKeys(); std::size_t const totalValidators = trustedKeys.size(); - std::size_t laggards = adaptor_.laggards(previousLedger_.seq(), trustedKeys); + std::size_t const laggards = adaptor_.laggards(previousLedger_.seq(), trustedKeys); std::size_t const offline = trustedKeys.size(); std::stringstream vars; @@ -1408,7 +1408,7 @@ this. inline int participantsNeeded(int participants, int percent) { - int result = ((participants * percent) + (percent / 2)) / 100; + int const result = ((participants * percent) + (percent / 2)) / 100; return (result == 0) ? 1 : result; } @@ -1757,7 +1757,7 @@ Consensus::createDisputes(TxSet_t const& o, std::unique_ptrtxns.find(txId) && o.find(txId)), "xrpl::Consensus::createDisputes : has disputed transactions"); - Tx_t tx = inThisSet ? result_->txns.find(txId) : o.find(txId); + Tx_t const tx = inThisSet ? result_->txns.find(txId) : o.find(txId); auto txID = tx.id(); if (result_->disputes.find(txID) != result_->disputes.end()) diff --git a/src/xrpld/consensus/ConsensusTypes.h b/src/xrpld/consensus/ConsensusTypes.h index 2331c9dfbf..8aba48f34e 100644 --- a/src/xrpld/consensus/ConsensusTypes.h +++ b/src/xrpld/consensus/ConsensusTypes.h @@ -117,7 +117,7 @@ class ConsensusTimer { using time_point = std::chrono::steady_clock::time_point; time_point start_; - std::chrono::milliseconds dur_; + std::chrono::milliseconds dur_{}; public: std::chrono::milliseconds diff --git a/src/xrpld/consensus/DisputedTx.h b/src/xrpld/consensus/DisputedTx.h index 89cb5115bb..e8304f4242 100644 --- a/src/xrpld/consensus/DisputedTx.h +++ b/src/xrpld/consensus/DisputedTx.h @@ -39,7 +39,7 @@ public: @param j Journal for debugging */ DisputedTx(Tx_t const& tx, bool ourVote, std::size_t numPeers, beast::Journal j) - : yays_(0), nays_(0), ourVote_(ourVote), tx_(tx), j_(j) + : ourVote_(ourVote), tx_(tx), j_(j) { votes_.reserve(numPeers); } @@ -97,7 +97,7 @@ public: // Compute the percentage of nodes voting 'yes' (possibly including us) int const support = (yays_ + (proposing && ourVote_ ? 1 : 0)) * 100; - int total = nays_ + yays_ + (proposing ? 1 : 0); + int const total = nays_ + yays_ + (proposing ? 1 : 0); if (!total) // There are no votes, so we know nothing return false; @@ -173,8 +173,8 @@ public: getJson() const; private: - int yays_; //< Number of yes votes - int nays_; //< Number of no votes + int yays_{0}; //< Number of yes votes + int nays_{0}; //< Number of no votes bool ourVote_; //< Our vote (true is yes) Tx_t tx_; //< Transaction under dispute Map_t votes_; //< Map from NodeID to vote @@ -258,8 +258,8 @@ DisputedTx::updateVote(int percentTime, bool proposing, Consensu if (!ourVote_ && (yays_ == 0)) return false; - bool newPosition; - int weight; + bool newPosition = false; + int weight = 0; // When proposing, to prevent avalanche stalls, we increase the needed // weight slightly over time. We also need to ensure that the consensus has diff --git a/src/xrpld/consensus/LedgerTrie.h b/src/xrpld/consensus/LedgerTrie.h index d34dda8e1b..aecd105c07 100644 --- a/src/xrpld/consensus/LedgerTrie.h +++ b/src/xrpld/consensus/LedgerTrie.h @@ -72,7 +72,7 @@ public: XRPL_ASSERT(ledger_.seq() == start_, "xrpl::Span::Span : ledger is genesis"); } - Span(Ledger ledger) : start_{0}, end_{ledger.seq() + Seq{1}}, ledger_{std::move(ledger)} + Span(Ledger ledger) : end_{ledger.seq() + Seq{1}}, ledger_{std::move(ledger)} { } @@ -128,7 +128,7 @@ public: SpanTip tip() const { - Seq tipSeq{end_ - Seq{1}}; + Seq const tipSeq{end_ - Seq{1}}; return SpanTip{tipSeq, ledger_[tipSeq], ledger_}; } @@ -149,8 +149,8 @@ private: std::optional sub(Seq from, Seq to) const { - Seq newFrom = clamp(from); - Seq newTo = clamp(to); + Seq const newFrom = clamp(from); + Seq const newTo = clamp(to); if (newFrom < newTo) return Span(newFrom, newTo, ledger_); return std::nullopt; @@ -344,6 +344,7 @@ class LedgerTrie std::pair find(Ledger const& ledger) const { + // NOLINTNEXTLINE(misc-const-correctness) Node* curr = root.get(); // Root is always defined and is in common with all ledgers diff --git a/src/xrpld/consensus/Validations.h b/src/xrpld/consensus/Validations.h index 4cd922d6b2..4d0b64a350 100644 --- a/src/xrpld/consensus/Validations.h +++ b/src/xrpld/consensus/Validations.h @@ -422,7 +422,7 @@ private: checkAcquired(lock); - std::pair valPair{val.seq(), val.ledgerID()}; + std::pair const valPair{val.seq(), val.ledgerID()}; auto it = acquiring_.find(valPair); if (it != acquiring_.end()) { @@ -479,7 +479,7 @@ private: void current(std::lock_guard const& lock, Pre&& pre, F&& f) { - NetClock::time_point t = adaptor_.now(); + NetClock::time_point const t = adaptor_.now(); pre(current_.size()); auto it = current_.begin(); while (it != current_.end()) @@ -569,7 +569,7 @@ public: bool canValidateSeq(Seq const s) { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; return localSeqEnforcer_(byLedger_.clock().now(), s, parms_); } @@ -588,7 +588,7 @@ public: return ValStatus::stale; { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; // Check that validation sequence is greater than any non-expired // validations sequence from that validator; if it's not, perform @@ -645,7 +645,7 @@ public: if (!inserted) { // Replace existing only if this one is newer - Validation& oldVal = it->second; + Validation const& oldVal = it->second; if (val.signTime() > oldVal.signTime()) { std::pair old(oldVal.seq(), oldVal.ledgerID()); @@ -674,7 +674,7 @@ public: void setSeqToKeep(Seq const& low, Seq const& high) { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; XRPL_ASSERT(low < high, "xrpl::Validations::setSeqToKeep : valid inputs"); toKeep_ = {low, high}; } @@ -689,7 +689,7 @@ public: { auto const start = std::chrono::steady_clock::now(); { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; if (toKeep_) { // We only need to refresh the keep range when it's just about @@ -746,7 +746,7 @@ public: void trustChanged(hash_set const& added, hash_set const& removed) { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; for (auto& [nodeId, validation] : current_) { @@ -782,7 +782,7 @@ public: Json::Value getJsonTrie() const { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; return trie_.getJson(); } @@ -801,7 +801,7 @@ public: std::optional> getPreferred(Ledger const& curr) { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; std::optional> preferred = withTrie(lock, [this](LedgerTrie& trie) { return trie.getPreferred(localSeqEnforcer_.largest()); }); @@ -913,7 +913,7 @@ public: std::size_t getNodesAfter(Ledger const& ledger, ID const& ledgerID) { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; // Use trie if ledger is the right one if (ledger.id() == ledgerID) @@ -936,7 +936,7 @@ public: currentTrusted() { std::vector ret; - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; current( lock, [&](std::size_t numValidations) { ret.reserve(numValidations); }, @@ -955,7 +955,7 @@ public: getCurrentNodeIDs() -> hash_set { hash_set ret; - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; current( lock, [&](std::size_t numValidations) { ret.reserve(numValidations); }, @@ -973,7 +973,7 @@ public: numTrustedForLedger(ID const& ledgerID) { std::size_t count = 0; - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; byLedger( lock, ledgerID, @@ -995,7 +995,7 @@ public: getTrustedForLedger(ID const& ledgerID, Seq const& seq) { std::vector res; - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; byLedger( lock, ledgerID, @@ -1018,7 +1018,7 @@ public: fees(ID const& ledgerID, std::uint32_t baseFee) { std::vector res; - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; byLedger( lock, ledgerID, @@ -1041,7 +1041,7 @@ public: void flush() { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; current_.clear(); } @@ -1084,28 +1084,28 @@ public: std::size_t sizeOfCurrentCache() const { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; return current_.size(); } std::size_t sizeOfSeqEnforcersCache() const { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; return seqEnforcers_.size(); } std::size_t sizeOfByLedgerCache() const { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; return byLedger_.size(); } std::size_t sizeOfBySequenceCache() const { - std::lock_guard lock{mutex_}; + std::lock_guard const lock{mutex_}; return bySequence_.size(); } }; diff --git a/src/xrpld/core/Config.h b/src/xrpld/core/Config.h index c40d13c83a..d4d8396ba5 100644 --- a/src/xrpld/core/Config.h +++ b/src/xrpld/core/Config.h @@ -5,6 +5,7 @@ #include #include #include +#include #include // VFALCO Breaks levelization #include @@ -57,6 +58,13 @@ struct FeeSetup /* (Remember to update the example cfg files when changing any of these * values.) */ + + /** Convert to a Fees object for use with Ledger construction. */ + Fees + toFees() const + { + return Fees{reference_fee, account_reserve, owner_reserve}; + } }; // This entire derived class is deprecated. @@ -126,7 +134,7 @@ public: // Entries from [ips_fixed] config stanza std::vector IPS_FIXED; - StartUpType START_UP = StartUpType::NORMAL; + StartUpType START_UP = StartUpType::Normal; bool START_VALID = false; @@ -137,10 +145,6 @@ public: // Network parameters uint32_t NETWORK_ID = 0; - // DEPRECATED - Fee units for a reference transaction. - // Only provided for backwards compatibility in a couple of places - static constexpr std::uint32_t FEE_UNITS_DEPRECATED = 10; - // 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; @@ -269,10 +273,10 @@ public: // First, attempt to load the latest ledger directly from disk. bool FAST_LOAD = false; - // When starting rippled with existing database it do not know it has those + // 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 rippled + // 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; diff --git a/src/xrpld/core/TimeKeeper.h b/src/xrpld/core/TimeKeeper.h index 72c4468e1f..0d809d076d 100644 --- a/src/xrpld/core/TimeKeeper.h +++ b/src/xrpld/core/TimeKeeper.h @@ -34,7 +34,7 @@ public: protocol, but it is possible for them to make an educated guess if this server publishes proposals or validations. - @note The network time is adjusted for the "Ripple epoch" which + @note The network time is adjusted for the "XRPL epoch" which was arbitrarily defined as 2000-01-01T00:00:00Z by Arthur Britto and David Schwartz during early development of the code. No rationale has been provided for this curious and diff --git a/src/xrpld/core/detail/Config.cpp b/src/xrpld/core/detail/Config.cpp index ce4ba43cd8..204b29ad30 100644 --- a/src/xrpld/core/detail/Config.cpp +++ b/src/xrpld/core/detail/Config.cpp @@ -51,7 +51,7 @@ namespace detail { [[nodiscard]] std::uint64_t getMemorySize() { - if (struct sysinfo si; sysinfo(&si) == 0) + if (struct sysinfo si{}; sysinfo(&si) == 0) return static_cast(si.totalram) * si.mem_unit; return 0; @@ -112,11 +112,12 @@ sizedItems {SizedItem::ramSizeGB, {{ 6, 8, 12, 24, 0 }}}, {SizedItem::accountIdCacheSize, {{ 20047, 50053, 77081, 150061, 300007 }}} }}; +// clang-format on // Ensure that the order of entries in the table corresponds to the // order of entries in the enum: static_assert( - []() constexpr->bool { + []() constexpr -> bool { std::underlying_type_t idx = 0; for (auto const& i : sizedItems) @@ -130,7 +131,6 @@ static_assert( return true; }(), "Mismatch between sized item enum & array indices"); -// clang-format on // // TODO: Check permissions on config file before using it. @@ -154,7 +154,7 @@ parseIniFile(std::string const& strInput, bool const bTrim) boost::algorithm::split(vLines, strData, boost::algorithm::is_any_of("\n")); // Set the default Section name. - std::string strSection = SECTION_DEFAULT_NAME; + std::string strSection = SECTION_DEFAULT_NAME; // NOLINT(readability-redundant-string-init) // Initialize the default Section. secResult[strSection] = IniFileSections::mapped_type(); @@ -204,13 +204,13 @@ getSingleSection( { auto const pmtEntries = getIniFileSection(secSource, strSection); - if (pmtEntries && pmtEntries->size() == 1) + if ((pmtEntries != nullptr) && pmtEntries->size() == 1) { strValue = (*pmtEntries)[0]; return true; } - if (pmtEntries) + if (pmtEntries != nullptr) { JLOG(j.warn()) << "Section '" << strSection << "': requires 1 line not " << pmtEntries->size() << " lines."; @@ -364,9 +364,13 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand // load() may have set a new value for the dataDir std::string const dbPath(legacy("database_path")); if (!dbPath.empty()) + { dataDir = boost::filesystem::path(dbPath); + } else if (RUN_STANDALONE) + { dataDir.clear(); + } } if (!dataDir.empty()) @@ -386,10 +390,10 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand if (RUN_STANDALONE) LEDGER_HISTORY = 0; - Section ledgerTxTablesSection = section("ledger_tx_tables"); + Section const ledgerTxTablesSection = section("ledger_tx_tables"); get_if_exists(ledgerTxTablesSection, "use_tx_tables", USE_TX_TABLES); - Section& nodeDbSection{section(ConfigSection::nodeDatabase())}; + Section const& nodeDbSection{section(ConfigSection::nodeDatabase())}; get_if_exists(nodeDbSection, "fast_load", FAST_LOAD); } @@ -411,7 +415,7 @@ checkZeroPorts(Config const& config) if (optResult) { auto const port = beast::lexicalCast(*optResult); - if (!port) + if (port == 0u) { std::stringstream ss; ss << "Invalid value '" << *optResult << "' for key 'port' in [" << name << "]"; @@ -467,7 +471,7 @@ Config::loadFromString(std::string const& fileContents) if (std::count(line.begin(), line.end(), ':') != 1) continue; - std::string result = std::regex_replace(line, e, " $1"); + std::string const result = std::regex_replace(line, e, " $1"); // sanity check the result of the replace, should be same length // as input if (result.size() == line.size()) @@ -483,7 +487,7 @@ Config::loadFromString(std::string const& fileContents) std::string dbPath; if (getSingleSection(secConfig, "database_path", dbPath, j_)) { - boost::filesystem::path p(dbPath); + boost::filesystem::path const p(dbPath); legacy("database_path", boost::filesystem::absolute(p).string()); } } @@ -493,13 +497,21 @@ Config::loadFromString(std::string const& fileContents) if (getSingleSection(secConfig, SECTION_NETWORK_ID, strTemp, j_)) { if (strTemp == "main") + { NETWORK_ID = 0; + } else if (strTemp == "testnet") + { NETWORK_ID = 1; + } else if (strTemp == "devnet") + { NETWORK_ID = 2; + } else + { NETWORK_ID = beast::lexicalCastThrow(strTemp); + } } if (getSingleSection(secConfig, SECTION_PEER_PRIVATE, strTemp, j_)) @@ -516,8 +528,10 @@ Config::loadFromString(std::string const& fileContents) { peers_in_max = beast::lexicalCastThrow(strTemp); if (*peers_in_max > 1000) + { Throw("Invalid value specified in [" SECTION_PEERS_IN_MAX "] section; the value must be less or equal than 1000"); + } } std::optional peers_out_max{}; @@ -525,15 +539,19 @@ Config::loadFromString(std::string const& fileContents) { peers_out_max = beast::lexicalCastThrow(strTemp); if (*peers_out_max < 10 || *peers_out_max > 1000) + { Throw("Invalid value specified in [" SECTION_PEERS_OUT_MAX "] section; the value must be in range 10-1000"); + } } // if one section is configured then the other must be configured too if ((peers_in_max && !peers_out_max) || (peers_out_max && !peers_in_max)) + { Throw("Both sections [" SECTION_PEERS_IN_MAX "]" "and [" SECTION_PEERS_OUT_MAX "] must be configured"); + } if (peers_in_max && peers_out_max) { @@ -545,17 +563,29 @@ Config::loadFromString(std::string const& fileContents) if (getSingleSection(secConfig, SECTION_NODE_SIZE, strTemp, j_)) { if (boost::iequals(strTemp, "tiny")) + { NODE_SIZE = 0; + } else if (boost::iequals(strTemp, "small")) + { NODE_SIZE = 1; + } else if (boost::iequals(strTemp, "medium")) + { NODE_SIZE = 2; + } else if (boost::iequals(strTemp, "large")) + { NODE_SIZE = 3; + } else if (boost::iequals(strTemp, "huge")) + { NODE_SIZE = 4; + } else + { NODE_SIZE = std::min(4, beast::lexicalCastThrow(strTemp)); + } } if (getSingleSection(secConfig, SECTION_SIGNING_SUPPORT, strTemp, j_)) @@ -573,32 +603,50 @@ Config::loadFromString(std::string const& fileContents) if (getSingleSection(secConfig, SECTION_RELAY_VALIDATIONS, strTemp, j_)) { if (boost::iequals(strTemp, "all")) + { RELAY_UNTRUSTED_VALIDATIONS = 1; + } else if (boost::iequals(strTemp, "trusted")) + { RELAY_UNTRUSTED_VALIDATIONS = 0; + } else if (boost::iequals(strTemp, "drop_untrusted")) + { RELAY_UNTRUSTED_VALIDATIONS = -1; + } else + { Throw("Invalid value specified in [" SECTION_RELAY_VALIDATIONS "] section"); + } } if (getSingleSection(secConfig, SECTION_RELAY_PROPOSALS, strTemp, j_)) { if (boost::iequals(strTemp, "all")) + { RELAY_UNTRUSTED_PROPOSALS = 1; + } else if (boost::iequals(strTemp, "trusted")) + { RELAY_UNTRUSTED_PROPOSALS = 0; + } else if (boost::iequals(strTemp, "drop_untrusted")) + { RELAY_UNTRUSTED_PROPOSALS = -1; + } else + { Throw("Invalid value specified in [" SECTION_RELAY_PROPOSALS "] section"); + } } if (exists(SECTION_VALIDATION_SEED) && exists(SECTION_VALIDATOR_TOKEN)) + { Throw("Cannot have both [" SECTION_VALIDATION_SEED "] and [" SECTION_VALIDATOR_TOKEN "] config sections"); + } if (getSingleSection(secConfig, SECTION_NETWORK_QUORUM, strTemp, j_)) NETWORK_QUORUM = beast::lexicalCastThrow(strTemp); @@ -613,24 +661,35 @@ Config::loadFromString(std::string const& fileContents) if (getSingleSection(secConfig, SECTION_LEDGER_HISTORY, strTemp, j_)) { if (boost::iequals(strTemp, "full")) + { LEDGER_HISTORY = std::numeric_limits::max(); + } else if (boost::iequals(strTemp, "none")) + { LEDGER_HISTORY = 0; + } else + { LEDGER_HISTORY = beast::lexicalCastThrow(strTemp); + } } if (getSingleSection(secConfig, SECTION_FETCH_DEPTH, strTemp, j_)) { if (boost::iequals(strTemp, "none")) + { FETCH_DEPTH = 0; + } else if (boost::iequals(strTemp, "full")) + { FETCH_DEPTH = std::numeric_limits::max(); + } else + { FETCH_DEPTH = beast::lexicalCastThrow(strTemp); + } - if (FETCH_DEPTH < 10) - FETCH_DEPTH = 10; + FETCH_DEPTH = std::max(FETCH_DEPTH, 10); } // By default, validators don't have pathfinding enabled, unless it is @@ -655,8 +714,10 @@ Config::loadFromString(std::string const& fileContents) SWEEP_INTERVAL = beast::lexicalCastThrow(strTemp); if (SWEEP_INTERVAL < 10 || SWEEP_INTERVAL > 600) + { Throw("Invalid " SECTION_SWEEP_INTERVAL ": must be between 10 and 600 inclusive"); + } } if (getSingleSection(secConfig, SECTION_WORKERS, strTemp, j_)) @@ -664,8 +725,10 @@ Config::loadFromString(std::string const& fileContents) WORKERS = beast::lexicalCastThrow(strTemp); if (WORKERS < 1 || WORKERS > 1024) + { Throw("Invalid " SECTION_WORKERS ": must be between 1 and 1024 inclusive."); + } } if (getSingleSection(secConfig, SECTION_IO_WORKERS, strTemp, j_)) @@ -673,8 +736,10 @@ Config::loadFromString(std::string const& fileContents) IO_WORKERS = beast::lexicalCastThrow(strTemp); if (IO_WORKERS < 1 || IO_WORKERS > 1024) + { Throw("Invalid " SECTION_IO_WORKERS ": must be between 1 and 1024 inclusive."); + } } if (getSingleSection(secConfig, SECTION_PREFETCH_WORKERS, strTemp, j_)) @@ -682,8 +747,10 @@ Config::loadFromString(std::string const& fileContents) PREFETCH_WORKERS = beast::lexicalCastThrow(strTemp); if (PREFETCH_WORKERS < 1 || PREFETCH_WORKERS > 1024) + { Throw("Invalid " SECTION_PREFETCH_WORKERS ": must be between 1 and 1024 inclusive."); + } } if (getSingleSection(secConfig, SECTION_COMPRESSION, strTemp, j_)) @@ -703,18 +770,26 @@ Config::loadFromString(std::string const& fileContents) // 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")) + { 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"); + } if (sec.exists("vp_base_squelch_enable")) + { VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = sec.value_or("vp_base_squelch_enable", false); + } else if (sec.exists("vp_enable")) + { VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = sec.value_or("vp_enable", false); + } else + { VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = false; + } ///////////////// !!END OF TEMPORARY CODE BLOCK!! ///////////////////// ///////////////////// !!TEMPORARY CODE BLOCK!! /////////////////////// @@ -724,9 +799,11 @@ Config::loadFromString(std::string const& fileContents) VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS = sec.value_or("vp_base_squelch_max_selected_peers", 5); if (VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS < 3) + { Throw("Invalid " SECTION_REDUCE_RELAY " vp_base_squelch_max_selected_peers must be " "greater than or equal to 3"); + } ///////////////// !!END OF TEMPORARY CODE BLOCK!! ///////////////////// TX_REDUCE_RELAY_ENABLE = sec.value_or("tx_enable", false); @@ -734,10 +811,12 @@ Config::loadFromString(std::string const& fileContents) TX_REDUCE_RELAY_MIN_PEERS = sec.value_or("tx_min_peers", 20); TX_RELAY_PERCENTAGE = sec.value_or("tx_relay_percentage", 25); if (TX_RELAY_PERCENTAGE < 10 || TX_RELAY_PERCENTAGE > 100 || TX_REDUCE_RELAY_MIN_PEERS < 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"); + } } if (getSingleSection(secConfig, SECTION_MAX_TRANSACTIONS, strTemp, j_)) @@ -776,9 +855,11 @@ Config::loadFromString(std::string const& fileContents) } if (MAX_UNKNOWN_TIME < seconds{300} || MAX_UNKNOWN_TIME > seconds{1800}) + { Throw( "Invalid value 'max_unknown_time' in " SECTION_OVERLAY ": the time must be between 300 and 1800 seconds, inclusive."); + } try { @@ -804,24 +885,36 @@ Config::loadFromString(std::string const& fileContents) 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]"); + } - std::uint32_t duration = beast::lexicalCastThrow(match[1].str()); + std::uint32_t const duration = beast::lexicalCastThrow(match[1].str()); if (boost::iequals(match[2], "minutes")) + { AMENDMENT_MAJORITY_TIME = minutes(duration); + } else if (boost::iequals(match[2], "hours")) + { AMENDMENT_MAJORITY_TIME = hours(duration); + } else if (boost::iequals(match[2], "days")) + { AMENDMENT_MAJORITY_TIME = days(duration); + } else if (boost::iequals(match[2], "weeks")) + { AMENDMENT_MAJORITY_TIME = weeks(duration); + } if (AMENDMENT_MAJORITY_TIME < minutes(15)) + { Throw("Invalid " SECTION_AMENDMENT_MAJORITY_TIME ", the minimum amount of time an amendment must hold a " "majority is 15 minutes"); + } } if (getSingleSection(secConfig, SECTION_BETA_RPC_API, strTemp, j_)) @@ -845,25 +938,30 @@ Config::loadFromString(std::string const& fileContents) validatorsFile = strTemp; if (validatorsFile.empty()) + { Throw("Invalid path specified in [" SECTION_VALIDATORS_FILE "]"); + } if (!validatorsFile.is_absolute() && !CONFIG_DIR.empty()) validatorsFile = CONFIG_DIR / validatorsFile; if (!boost::filesystem::exists(validatorsFile)) + { Throw( "The file specified in [" SECTION_VALIDATORS_FILE "] " "does not exist: " + validatorsFile.string()); - + } else if ( !boost::filesystem::is_regular_file(validatorsFile) && !boost::filesystem::is_symlink(validatorsFile)) + { Throw( "Invalid file specified in [" SECTION_VALIDATORS_FILE "]: " + validatorsFile.string()); + } } else if (!CONFIG_DIR.empty()) { @@ -872,11 +970,15 @@ Config::loadFromString(std::string const& fileContents) if (!validatorsFile.empty()) { if (!boost::filesystem::exists(validatorsFile)) + { validatorsFile.clear(); + } else if ( !boost::filesystem::is_regular_file(validatorsFile) && !boost::filesystem::is_symlink(validatorsFile)) + { validatorsFile.clear(); + } } } @@ -897,30 +999,31 @@ Config::loadFromString(std::string const& fileContents) auto entries = getIniFileSection(iniFile, SECTION_VALIDATORS); - if (entries) + if (entries != nullptr) section(SECTION_VALIDATORS).append(*entries); auto valKeyEntries = getIniFileSection(iniFile, SECTION_VALIDATOR_KEYS); - if (valKeyEntries) + if (valKeyEntries != nullptr) section(SECTION_VALIDATOR_KEYS).append(*valKeyEntries); auto valSiteEntries = getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_SITES); - if (valSiteEntries) + if (valSiteEntries != nullptr) section(SECTION_VALIDATOR_LIST_SITES).append(*valSiteEntries); auto valListKeys = getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_KEYS); - if (valListKeys) + if (valListKeys != nullptr) section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys); auto valListThreshold = getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_THRESHOLD); - if (valListThreshold) + if (valListThreshold != nullptr) section(SECTION_VALIDATOR_LIST_THRESHOLD).append(*valListThreshold); - if (!entries && !valKeyEntries && !valListKeys) + if ((entries == nullptr) && (valKeyEntries == nullptr) && (valListKeys == nullptr)) + { Throw( "The file specified in [" SECTION_VALIDATORS_FILE "] " @@ -932,19 +1035,24 @@ Config::loadFromString(std::string const& fileContents) "]" " section: " + validatorsFile.string()); + } } VALIDATOR_LIST_THRESHOLD = [&]() -> std::optional { auto const& listThreshold = section(SECTION_VALIDATOR_LIST_THRESHOLD); if (listThreshold.lines().empty()) + { return std::nullopt; - else if (listThreshold.values().size() == 1) + } + if (listThreshold.values().size() == 1) { auto strTemp = listThreshold.values()[0]; auto const listThreshold = beast::lexicalCastThrow(strTemp); if (listThreshold == 0) + { return std::nullopt; // NOTE: Explicitly ask for computed - else if (listThreshold > section(SECTION_VALIDATOR_LIST_KEYS).values().size()) + } + if (listThreshold > section(SECTION_VALIDATOR_LIST_KEYS).values().size()) { Throw( "Value in config section " @@ -953,12 +1061,10 @@ Config::loadFromString(std::string const& fileContents) } return listThreshold; } - else - { - Throw( - "Config section " - "[" SECTION_VALIDATOR_LIST_THRESHOLD "] should contain single value only"); - } + + Throw( + "Config section " + "[" SECTION_VALIDATOR_LIST_THRESHOLD "] should contain single value only"); }(); // Consolidate [validator_keys] and [validators] @@ -977,9 +1083,13 @@ Config::loadFromString(std::string const& fileContents) for (auto const& s : part.values()) { if (auto const f = getRegisteredFeature(s)) + { features.insert(*f); + } else + { Throw("Unknown feature: " + s + " in config file."); + } } } @@ -1050,13 +1160,13 @@ setup_FeeVote(Section const& section) { FeeSetup setup; { - std::uint64_t temp; + std::uint64_t temp = 0; if (set(temp, "reference_fee", section) && temp <= std::numeric_limits::max()) setup.reference_fee = temp; } { - std::uint32_t temp; + std::uint32_t temp = 0; if (set(temp, "account_reserve", section)) setup.account_reserve = temp; if (set(temp, "owner_reserve", section)) @@ -1116,7 +1226,7 @@ setup_DatabaseCon(Config const& c, std::optional j) "Configuration file may not define both " "\"safety_level\" and \"journal_mode\""); } - bool higherRisk = + bool const higherRisk = boost::iequals(journal_mode, "memory") || boost::iequals(journal_mode, "off"); showRiskWarning = showRiskWarning || higherRisk; if (higherRisk || boost::iequals(journal_mode, "delete") || @@ -1140,7 +1250,7 @@ setup_DatabaseCon(Config const& c, std::optional j) "Configuration file may not define both " "\"safety_level\" and \"synchronous\""); } - bool higherRisk = boost::iequals(synchronous, "off"); + bool const higherRisk = boost::iequals(synchronous, "off"); showRiskWarning = showRiskWarning || higherRisk; if (higherRisk || boost::iequals(synchronous, "normal") || boost::iequals(synchronous, "full") || boost::iequals(synchronous, "extra")) @@ -1161,7 +1271,7 @@ setup_DatabaseCon(Config const& c, std::optional j) "Configuration file may not define both " "\"safety_level\" and \"temp_store\""); } - bool higherRisk = boost::iequals(temp_store, "memory"); + bool const higherRisk = boost::iequals(temp_store, "memory"); showRiskWarning = showRiskWarning || higherRisk; if (higherRisk || boost::iequals(temp_store, "default") || boost::iequals(temp_store, "file")) @@ -1205,7 +1315,7 @@ setup_DatabaseCon(Config const& c, std::optional j) if (page_size < 512 || page_size > 65536) Throw("Invalid page_size. Must be between 512 and 65536."); - if (page_size & (page_size - 1)) + if ((page_size & (page_size - 1)) != 0) Throw("Invalid page_size. Must be a power of 2."); } diff --git a/src/xrpld/overlay/Message.h b/src/xrpld/overlay/Message.h index 30650d384c..df59656ac2 100644 --- a/src/xrpld/overlay/Message.h +++ b/src/xrpld/overlay/Message.h @@ -91,7 +91,7 @@ private: * currently LZ4 only. If None then the message is uncompressed. * @param uncompressedBytes Size of the uncompressed message */ - void + static void setHeader( std::uint8_t* in, std::uint32_t payloadBytes, @@ -112,8 +112,8 @@ private: * @param in Payload header pointer * @return Message type */ - int - getType(std::uint8_t const* in) const; + static int + getType(std::uint8_t const* in); }; } // namespace xrpl diff --git a/src/xrpld/overlay/Overlay.h b/src/xrpld/overlay/Overlay.h index f1d1104d4e..7d2508a584 100644 --- a/src/xrpld/overlay/Overlay.h +++ b/src/xrpld/overlay/Overlay.h @@ -18,7 +18,7 @@ namespace boost { namespace asio { namespace ssl { class context; -} +} // namespace ssl } // namespace asio } // namespace boost diff --git a/src/xrpld/overlay/Peer.h b/src/xrpld/overlay/Peer.h index a0e4c040fd..df2cc5bcb7 100644 --- a/src/xrpld/overlay/Peer.h +++ b/src/xrpld/overlay/Peer.h @@ -11,7 +11,7 @@ namespace xrpl { namespace Resource { class Charge; -} +} // namespace Resource enum class ProtocolFeature { ValidatorListPropagation, diff --git a/src/xrpld/overlay/README.md b/src/xrpld/overlay/README.md index 51eb96a001..bda0027ea7 100644 --- a/src/xrpld/overlay/README.md +++ b/src/xrpld/overlay/README.md @@ -3,11 +3,11 @@ ## Introduction The _XRP Ledger network_ consists of a collection of _peers_ running -**`rippled`** or other compatible software. Each peer maintains multiple +**`xrpld`** or other compatible software. Each peer maintains multiple outgoing connections and optional incoming connections to other peers. These connections are made over both the public Internet and private local area networks. This network defines a connected directed graph of nodes -where vertices are instances of `rippled` and edges are persistent TCP/IP +where vertices are instances of `xrpld` and edges are persistent TCP/IP connections. Peers send and receive messages to other connected peers. This peer to peer network, layered on top of the public and private Internet, forms an [_overlay network_][overlay_network]. The contents of the messages @@ -56,7 +56,7 @@ failed (e.g. by sending HTTP 400 "Bad Request" or HTTP 503 "Service Unavailable" ``` GET / HTTP/1.1 -User-Agent: rippled-1.4.0-b1+DEBUG +User-Agent: xrpld-1.4.0-b1+DEBUG Upgrade: RTXP/1.2, XRPL/2.0 Connection: Upgrade Connect-As: Peer @@ -77,7 +77,7 @@ HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: RTXP/1.2 Connect-As: Peer -Server: rippled-1.3.1 +Server: xrpld-1.3.1 Crawl: public Public-Key: n9K1ZXXXzzA3dtgKBuQUnZXkhygMRgZbSo3diFNPVHLMsUG5osJM Session-Signature: MEQCIHMlLGTcGyPvHji7WY2nRM2B0iSBnw9xeDUGW7bPq7IjAiAmy+ofEu+8nOq2eChRTr3wjoKi3EYRqLgzP+q+ORFcig== @@ -90,7 +90,7 @@ Previous-Ledger: EPvIpAD2iavGFyyZYi8REexAXyKGXsi1jMF7OIBY6/Y= ``` HTTP/1.1 503 Service Unavailable -Server: rippled-0.27.0 +Server: xrpld-0.27.0 Remote-Address: 63.104.209.13 Content-Length: 253 Content-Type: application/json @@ -354,9 +354,9 @@ transferred between A and B and will not be able to intelligently tamper with th message stream between Alice and Bob, although she may be still be able to inject delays or terminate the link. -# Ripple Clustering +# XRPL clustering -A cluster consists of more than one Ripple server under common +A cluster consists of more than one XRPL server under common administration that share load information, distribute cryptography operations, and provide greater response consistency. diff --git a/src/xrpld/overlay/Slot.h b/src/xrpld/overlay/Slot.h index ac3c3ad49e..22e908ee99 100644 --- a/src/xrpld/overlay/Slot.h +++ b/src/xrpld/overlay/Slot.h @@ -98,9 +98,7 @@ private: * validator message source */ Slot(SquelchHandler const& handler, beast::Journal journal, uint16_t maxSelectedPeers) - : reachedThreshold_(0) - , lastSelected_(clock_type::now()) - , state_(SlotState::Counting) + : lastSelected_(clock_type::now()) , handler_(handler) , journal_(journal) , maxSelectedPeers_(maxSelectedPeers) @@ -220,14 +218,14 @@ private: std::unordered_set considered_; // number of peers that reached MAX_MESSAGE_THRESHOLD - std::uint16_t reachedThreshold_; + std::uint16_t reachedThreshold_{0}; // last time peers were selected, used to age the slot typename clock_type::time_point lastSelected_; - SlotState state_; // slot's state - SquelchHandler const& handler_; // squelch/unsquelch handler - beast::Journal const journal_; // logging + SlotState state_{SlotState::Counting}; // slot's state + SquelchHandler const& handler_; // squelch/unsquelch handler + beast::Journal const journal_; // logging // the maximum number of peers that should be selected as a validator // message source @@ -374,7 +372,7 @@ Slot::update( if (journal_.trace()) str << k << " "; v.state = PeerState::Squelched; - std::chrono::seconds duration = + std::chrono::seconds const duration = getSquelchDuration(peers_.size() - maxSelectedPeers_); v.expire = now + duration; handler_.squelch(validator, k, duration.count()); @@ -536,14 +534,14 @@ class Slots final public: /** - * @param logs reference to the logger + * @param registry The service registry. * @param handler Squelch/unsquelch implementation * @param config reference to the global config */ - Slots(Logs& logs, SquelchHandler const& handler, Config const& config) + Slots(ServiceRegistry& registry, SquelchHandler const& handler, Config const& config) : handler_(handler) - , logs_(logs) - , journal_(logs.journal("Slots")) + , logs_(registry.getLogs()) + , journal_(registry.getJournal("Slots")) , baseSquelchEnabled_(config.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE) , maxSelectedPeers_(config.VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS) { diff --git a/src/xrpld/overlay/detail/Cluster.cpp b/src/xrpld/overlay/detail/Cluster.cpp index 0ee633fb90..72b7ef5147 100644 --- a/src/xrpld/overlay/detail/Cluster.cpp +++ b/src/xrpld/overlay/detail/Cluster.cpp @@ -18,7 +18,7 @@ Cluster::Cluster(beast::Journal j) : j_(j) std::optional Cluster::member(PublicKey const& identity) const { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto iter = nodes_.find(identity); if (iter == nodes_.end()) @@ -29,7 +29,7 @@ Cluster::member(PublicKey const& identity) const std::size_t Cluster::size() const { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return nodes_.size(); } @@ -41,7 +41,7 @@ Cluster::update( std::uint32_t loadFee, NetClock::time_point reportTime) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto iter = nodes_.find(identity); @@ -63,7 +63,7 @@ Cluster::update( void Cluster::for_each(std::function func) const { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); for (auto const& ni : nodes_) func(ni); } diff --git a/src/xrpld/overlay/detail/ConnectAttempt.cpp b/src/xrpld/overlay/detail/ConnectAttempt.cpp index ac0743e936..40466f19b9 100644 --- a/src/xrpld/overlay/detail/ConnectAttempt.cpp +++ b/src/xrpld/overlay/detail/ConnectAttempt.cpp @@ -51,7 +51,10 @@ void ConnectAttempt::stop() { if (!strand_.running_in_this_thread()) - return boost::asio::post(strand_, std::bind(&ConnectAttempt::stop, shared_from_this())); + { + boost::asio::post(strand_, std::bind(&ConnectAttempt::stop, shared_from_this())); + return; + } if (!socket_.is_open()) return; @@ -65,7 +68,10 @@ void ConnectAttempt::run() { if (!strand_.running_in_this_thread()) - return boost::asio::post(strand_, std::bind(&ConnectAttempt::run, shared_from_this())); + { + boost::asio::post(strand_, std::bind(&ConnectAttempt::run, shared_from_this())); + return; + } JLOG(journal_.debug()) << "run: connecting to " << remote_endpoint_; @@ -115,9 +121,10 @@ ConnectAttempt::tryAsyncShutdown() if (currentStep_ != ConnectionStep::TcpConnect && currentStep_ != ConnectionStep::TlsHandshake) { setTimer(ConnectionStep::ShutdownStarted); - return stream_.async_shutdown(bind_executor( + stream_.async_shutdown(bind_executor( strand_, std::bind(&ConnectAttempt::onShutdown, shared_from_this(), std::placeholders::_1))); + return; } close(); @@ -136,7 +143,7 @@ ConnectAttempt::onShutdown(error_code ec) // 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 shouldLog = + 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); @@ -160,7 +167,7 @@ ConnectAttempt::close() cancelTimer(); error_code ec; - socket_.close(ec); + socket_.close(ec); // NOLINT(bugprone-unused-return-value) } void @@ -197,7 +204,8 @@ ConnectAttempt::setTimer(ConnectionStep step) catch (std::exception const& ex) { JLOG(journal_.error()) << "setTimer (global): " << ex.what(); - return close(); + close(); + return; } } @@ -240,7 +248,8 @@ ConnectAttempt::setTimer(ConnectionStep step) catch (std::exception const& ex) { JLOG(journal_.error()) << "setTimer (step " << stepToString(step) << "): " << ex.what(); - return close(); + close(); + return; } } @@ -272,13 +281,14 @@ ConnectAttempt::onTimer(error_code ec) // This should never happen JLOG(journal_.error()) << "onTimer: " << ec.message(); - return close(); + close(); + return; } // Determine which timer expired by checking their expiry times auto const now = std::chrono::steady_clock::now(); - bool globalExpired = (timer_.expiry() <= now); - bool stepExpired = (stepTimer_.expiry() <= now); + bool const globalExpired = (timer_.expiry() <= now); + bool const stepExpired = (stepTimer_.expiry() <= now); if (globalExpired) { @@ -304,9 +314,13 @@ ConnectAttempt::onConnect(error_code ec) if (ec) { if (ec == boost::asio::error::operation_aborted) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } - return fail("onConnect", ec); + fail("onConnect", ec); + return; } if (!socket_.is_open()) @@ -315,10 +329,16 @@ ConnectAttempt::onConnect(error_code ec) // check if connection has really been established socket_.local_endpoint(ec); if (ec) - return fail("onConnect", ec); + { + fail("onConnect", ec); + return; + } if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } ioPending_ = true; @@ -340,25 +360,38 @@ ConnectAttempt::onHandshake(error_code ec) if (ec) { if (ec == boost::asio::error::operation_aborted) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } - return fail("onHandshake", ec); + fail("onHandshake", ec); + return; } auto const local_endpoint = socket_.local_endpoint(ec); if (ec) - return fail("onHandshake", ec); + { + fail("onHandshake", ec); + return; + } setTimer(ConnectionStep::HttpWrite); // check if we connected to ourselves if (!overlay_.peerFinder().onConnected( slot_, beast::IPAddressConversion::from_asio(local_endpoint))) - return fail("Self connection"); + { + fail("Self connection"); + return; + } auto const sharedValue = makeSharedValue(*stream_ptr_, journal_); if (!sharedValue) - return shutdown(); // makeSharedValue logs + { + shutdown(); + return; // makeSharedValue logs + } req_ = makeRequest( !overlay_.peerFinder().config().peerPrivate, @@ -376,7 +409,10 @@ ConnectAttempt::onHandshake(error_code ec) app_); if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } ioPending_ = true; @@ -396,13 +432,20 @@ ConnectAttempt::onWrite(error_code ec) if (ec) { if (ec == boost::asio::error::operation_aborted) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } - return fail("onWrite", ec); + fail("onWrite", ec); + return; } if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } ioPending_ = true; @@ -429,17 +472,25 @@ ConnectAttempt::onRead(error_code ec) if (ec == boost::asio::error::eof) { JLOG(journal_.debug()) << "EOF"; - return shutdown(); + shutdown(); + return; } if (ec == boost::asio::error::operation_aborted) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } - return fail("onRead", ec); + fail("onRead", ec); + return; } if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } processResponse(); } @@ -457,7 +508,8 @@ ConnectAttempt::processResponse() { JLOG(journal_.warn()) << "Unable to upgrade to peer protocol: " << response_.result() << " (" << response_.reason() << ")"; - return shutdown(); + shutdown(); + return; } // Parse response body to determine if this is a redirect or other @@ -465,8 +517,10 @@ ConnectAttempt::processResponse() std::string responseBody; responseBody.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)); + } Json::Value json; Json::Reader reader; @@ -481,12 +535,16 @@ ConnectAttempt::processResponse() << " failed to upgrade to peer protocol: " << response_.result() << " (" << response_.reason() << ")"; - return shutdown(); + shutdown(); + return; } Json::Value const& peerIps = json["peer-ips"]; if (!peerIps.isArray()) - return fail("processResponse: invalid peer-ips format"); + { + fail("processResponse: invalid peer-ips format"); + return; + } // Extract and validate peer endpoints std::vector redirectEndpoints; @@ -506,7 +564,8 @@ ConnectAttempt::processResponse() // Notify PeerFinder about the redirect redirectEndpoints may be empty overlay_.peerFinder().onRedirects(remote_endpoint_, redirectEndpoints); - return fail("processResponse: failed to connect to peer: redirected"); + fail("processResponse: failed to connect to peer: redirected"); + return; } // Just because our peer selected a particular protocol version doesn't @@ -520,12 +579,18 @@ ConnectAttempt::processResponse() negotiatedProtocol = pvs[0]; if (!negotiatedProtocol) - return fail("processResponse: Unable to negotiate protocol version"); + { + fail("processResponse: Unable to negotiate protocol version"); + return; + } } auto const sharedValue = makeSharedValue(*stream_ptr_, journal_); if (!sharedValue) - return shutdown(); // makeSharedValue logs + { + shutdown(); + return; // makeSharedValue logs + } try { @@ -542,7 +607,7 @@ ConnectAttempt::processResponse() JLOG(journal_.debug()) << "Protocol: " << to_string(*negotiatedProtocol); JLOG(journal_.info()) << "Public Key: " << toBase58(TokenType::NodePublic, publicKey); - auto const member = app_.cluster().member(publicKey); + auto const member = app_.getCluster().member(publicKey); if (member) { JLOG(journal_.info()) << "Cluster name: " << *member; @@ -553,14 +618,18 @@ ConnectAttempt::processResponse() { std::stringstream ss; ss << "Outbound Connect Attempt " << remote_endpoint_ << " " << to_string(result); - return fail(ss.str()); + fail(ss.str()); + return; } if (!socket_.is_open()) return; if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } auto const peer = std::make_shared( app_, @@ -578,7 +647,8 @@ ConnectAttempt::processResponse() } catch (std::exception const& e) { - return fail(std::string("Handshake failure (") + e.what() + ")"); + fail(std::string("Handshake failure (") + e.what() + ")"); + return; } } diff --git a/src/xrpld/overlay/detail/ConnectAttempt.h b/src/xrpld/overlay/detail/ConnectAttempt.h index a3b2fd5cce..520ebe277e 100644 --- a/src/xrpld/overlay/detail/ConnectAttempt.h +++ b/src/xrpld/overlay/detail/ConnectAttempt.h @@ -145,7 +145,7 @@ public: beast::Journal journal, OverlayImpl& overlay); - ~ConnectAttempt(); + virtual ~ConnectAttempt(); /** * @brief Stop the connection attempt diff --git a/src/xrpld/overlay/detail/Handshake.cpp b/src/xrpld/overlay/detail/Handshake.cpp index c463a31257..9d86724b82 100644 --- a/src/xrpld/overlay/detail/Handshake.cpp +++ b/src/xrpld/overlay/detail/Handshake.cpp @@ -23,7 +23,7 @@ getFeatureValue(boost::beast::http::fields const& headers, std::string const& fe if (header == headers.end()) return {}; boost::smatch match; - boost::regex rx(feature + "=([^;\\s]+)"); + boost::regex const rx(feature + "=([^;\\s]+)"); std::string const allFeatures(header->value()); if (boost::regex_search(allFeatures, match, rx)) return {match[1]}; @@ -99,7 +99,7 @@ makeFeaturesResponseHeader( @note This construct is non-standard. There are potential "standard" alternatives that should be considered. For a discussion, on this topic, see https://github.com/openssl/openssl/issues/5509 and - https://github.com/ripple/rippled/issues/2413. + https://github.com/XRPLF/rippled/issues/2413. */ static std::optional> hashLastMessage(SSL const* ssl, size_t (*get)(const SSL*, void*, size_t)) @@ -107,12 +107,12 @@ hashLastMessage(SSL const* ssl, size_t (*get)(const SSL*, void*, size_t)) constexpr std::size_t sslMinimumFinishedLength = 12; unsigned char buf[1024]; - size_t len = get(ssl, buf, sizeof(buf)); + size_t const len = get(ssl, buf, sizeof(buf)); if (len < sslMinimumFinishedLength) return std::nullopt; - sha512_hasher h; + sha512_hasher const h; base_uint<512> cookie; SHA512(buf, len, cookie.data()); @@ -166,7 +166,7 @@ buildHandshake( h.insert("Network-ID", std::to_string(*networkID)); } - h.insert("Network-Time", std::to_string(app.timeKeeper().now().time_since_epoch().count())); + h.insert("Network-Time", std::to_string(app.getTimeKeeper().now().time_since_epoch().count())); h.insert("Public-Key", toBase58(TokenType::NodePublic, app.nodeIdentity().first)); @@ -211,7 +211,7 @@ verifyHandshake( if (auto const iter = headers.find("Network-ID"); iter != headers.end()) { - std::uint32_t nid; + std::uint32_t nid = 0; if (!beast::lexicalCastChecked(nid, iter->value())) throw std::runtime_error("Invalid peer network identifier"); @@ -223,7 +223,7 @@ verifyHandshake( if (auto const iter = headers.find("Network-Time"); iter != headers.end()) { auto const netTime = [str = iter->value()]() -> TimeKeeper::time_point { - TimeKeeper::duration::rep val; + TimeKeeper::duration::rep val = 0; if (beast::lexicalCastChecked(val, str)) return TimeKeeper::time_point{TimeKeeper::duration{val}}; @@ -235,7 +235,7 @@ verifyHandshake( using namespace std::chrono; - auto const ourTime = app.timeKeeper().now(); + auto const ourTime = app.getTimeKeeper().now(); auto const tolerance = 20s; // We can't blindly "return a-b;" because TimeKeeper::time_point @@ -300,9 +300,11 @@ verifyHandshake( throw std::runtime_error("Invalid Local-IP"); if (beast::IP::is_public(remote) && remote != local_ip) + { throw std::runtime_error( "Incorrect Local-IP: " + remote.to_string() + " instead of " + local_ip.to_string()); + } } if (auto const iter = headers.find("Remote-IP"); iter != headers.end()) @@ -318,9 +320,11 @@ verifyHandshake( // We know our public IP and peer reports our connection came // from some other IP. if (remote_ip != public_ip) + { throw std::runtime_error( "Incorrect Remote-IP: " + public_ip.to_string() + " instead of " + remote_ip.to_string()); + } } } diff --git a/src/xrpld/overlay/detail/Message.cpp b/src/xrpld/overlay/detail/Message.cpp index 510bc24fe2..1f0c6f608d 100644 --- a/src/xrpld/overlay/detail/Message.cpp +++ b/src/xrpld/overlay/detail/Message.cpp @@ -58,6 +58,8 @@ Message::compress() bool const compressible = [&] { if (messageBytes <= 70) return false; + + // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (type) { case protocol::mtMANIFESTS: @@ -101,10 +103,13 @@ Message::compress() if (compressedSize < (messageBytes - (headerBytesCompressed - headerBytes))) { bufferCompressed_.resize(headerBytesCompressed + compressedSize); + // NOLINTNEXTLINE(readability-suspicious-call-argument) setHeader(bufferCompressed_.data(), compressedSize, type, Algorithm::LZ4, messageBytes); } else + { bufferCompressed_.resize(0); + } } } @@ -186,16 +191,18 @@ Message::getBuffer(Compressed tryCompressed) std::call_once(once_flag_, &Message::compress, this); - if (bufferCompressed_.size() > 0) + if (!bufferCompressed_.empty()) + { return bufferCompressed_; - else - return buffer_; + } + + return buffer_; } int -Message::getType(std::uint8_t const* in) const +Message::getType(std::uint8_t const* in) { - int type = (static_cast(*(in + 4)) << 8) + *(in + 5); + int const type = (static_cast(*(in + 4)) << 8) + *(in + 5); return type; } diff --git a/src/xrpld/overlay/detail/OverlayImpl.cpp b/src/xrpld/overlay/detail/OverlayImpl.cpp index 4379f1b265..10ebae6b0f 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.cpp +++ b/src/xrpld/overlay/detail/OverlayImpl.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include @@ -24,6 +24,8 @@ #include #include +#include + namespace xrpl { namespace CrawlOptions { @@ -34,7 +36,7 @@ enum { ServerCounts = (1 << 2), Unl = (1 << 3) }; -} +} // namespace CrawlOptions //------------------------------------------------------------------------------ @@ -112,20 +114,19 @@ OverlayImpl::OverlayImpl( , work_(std::in_place, boost::asio::make_work_guard(io_context_)) , strand_(boost::asio::make_strand(io_context_)) , setup_(setup) - , journal_(app_.journal("Overlay")) + , journal_(app_.getJournal("Overlay")) , serverHandler_(serverHandler) , m_resourceManager(resourceManager) , m_peerFinder( PeerFinder::make_Manager( io_context, stopwatch(), - app_.journal("PeerFinder"), + app_.getJournal("PeerFinder"), config, collector)) , m_resolver(resolver) , next_id_(1) - , timer_count_(0) - , slots_(app.logs(), *this, app.config()) + , slots_(app, *this, app.config()) , m_stats( std::bind(&OverlayImpl::collect_metrics, this), collector, @@ -148,8 +149,9 @@ OverlayImpl::onHandoff( endpoint_type remote_endpoint) { auto const id = next_id_++; - beast::WrappedSink sink(app_.logs()["Peer"], makePrefix(id)); - beast::Journal journal(sink); + auto peerJournal = app_.getJournal("Peer"); + beast::WrappedSink sink(peerJournal.sink(), makePrefix(id)); + beast::Journal const journal(sink); Handoff handoff; if (processRequest(request, handoff)) @@ -238,8 +240,8 @@ OverlayImpl::onHandoff( { // The node gets a reserved slot if it is in our cluster // or if it has a reservation. - bool const reserved = static_cast(app_.cluster().member(publicKey)) || - app_.peerReservations().contains(publicKey); + bool const reserved = static_cast(app_.getCluster().member(publicKey)) || + app_.getPeerReservations().contains(publicKey); auto const result = m_peerFinder->activate(slot, publicKey, reserved); if (result != PeerFinder::Result::success) { @@ -267,7 +269,7 @@ OverlayImpl::onHandoff( // As we are not on the strand, run() must be called // while holding the lock, otherwise new I/O can be // queued after a call to stop(). - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); { auto const result = m_peers.emplace(peer->slot(), peer); XRPL_ASSERT(result.second, "xrpl::OverlayImpl::onHandoff : peer is inserted"); @@ -387,10 +389,10 @@ OverlayImpl::connect(beast::IP::Endpoint const& remote_endpoint) setup_.context, next_id_++, slot, - app_.journal("Peer"), + app_.getJournal("Peer"), *this); - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); list_.emplace(p.get(), p); p->run(); } @@ -402,9 +404,9 @@ void OverlayImpl::add_active(std::shared_ptr const& peer) { beast::WrappedSink sink{journal_.sink(), peer->prefix()}; - beast::Journal journal{sink}; + beast::Journal const journal{sink}; - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); { auto const result = m_peers.emplace(peer->slot(), peer); @@ -432,7 +434,7 @@ OverlayImpl::add_active(std::shared_ptr const& peer) void OverlayImpl::remove(std::shared_ptr const& slot) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto const iter = m_peers.find(slot); XRPL_ASSERT(iter != m_peers.end(), "xrpl::OverlayImpl::remove : valid input"); m_peers.erase(iter); @@ -441,7 +443,7 @@ OverlayImpl::remove(std::shared_ptr const& slot) void OverlayImpl::start() { - PeerFinder::Config config = PeerFinder::Config::makeConfig( + PeerFinder::Config const config = PeerFinder::Config::makeConfig( app_.config(), serverHandler_.setup().overlay.port(), app_.getValidationPublicKey().has_value(), @@ -479,9 +481,13 @@ OverlayImpl::start() for (auto const& addr : addresses) { if (addr.port() == 0) + { ips.push_back(to_string(addr.at_port(DEFAULT_PEER_PORT))); + } else + { ips.push_back(to_string(addr)); + } } std::string const base("config: "); @@ -501,9 +507,13 @@ OverlayImpl::start() for (auto& addr : addresses) { if (addr.port() == 0) + { ips.emplace_back(addr.address(), DEFAULT_PEER_PORT); + } else + { ips.emplace_back(addr); + } } if (!ips.empty()) @@ -511,7 +521,7 @@ OverlayImpl::start() }); } auto const timer = std::make_shared(*this); - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); list_.emplace(timer.get(), timer); timer_ = timer; timer->async_wait(); @@ -560,11 +570,11 @@ void OverlayImpl::activate(std::shared_ptr const& peer) { beast::WrappedSink sink{journal_.sink(), peer->prefix()}; - beast::Journal journal{sink}; + beast::Journal const journal{sink}; // Now track this peer { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto const result(ids_.emplace( std::piecewise_construct, std::make_tuple(peer->id()), std::make_tuple(peer))); XRPL_ASSERT(result.second, "xrpl::OverlayImpl::activate : peer ID is inserted"); @@ -580,7 +590,7 @@ OverlayImpl::activate(std::shared_ptr const& peer) void OverlayImpl::onPeerDeactivate(Peer::id_t id) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); ids_.erase(id); } @@ -602,7 +612,7 @@ OverlayImpl::onManifests( { auto const serialized = mo->serialized; - auto const result = app_.validatorManifests().applyManifest(std::move(*mo)); + auto const result = app_.getValidatorManifests().applyManifest(std::move(*mo)); if (result == ManifestDisposition::accepted) { @@ -619,7 +629,7 @@ OverlayImpl::onManifests( app_.getOPs().pubManifest(*mo); - if (app_.validators().listed(mo->masterKey)) + if (app_.getValidators().listed(mo->masterKey)) { auto db = app_.getWalletDB().checkoutDb(); addValidatorManifest(*db, serialized); @@ -634,8 +644,10 @@ OverlayImpl::onManifests( } if (!relay.list().empty()) + { for_each([m2 = std::make_shared(relay, protocol::mtMANIFESTS)]( - std::shared_ptr&& p) { p->send(m2); }); + std::shared_ptr const& p) { p->send(m2); }); + } } void @@ -651,12 +663,12 @@ OverlayImpl::reportOutboundTraffic(TrafficCount::category cat, int size) } /** The number of active peers on the network Active peers are only those peers that have completed the handshake - and are running the Ripple protocol. + and are running the XRPL protocol. */ std::size_t OverlayImpl::size() const { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return ids_.size(); } @@ -667,16 +679,16 @@ OverlayImpl::limit() } Json::Value -OverlayImpl::getOverlayInfo() +OverlayImpl::getOverlayInfo() const { using namespace std::chrono; Json::Value jv; - auto& av = jv["active"] = Json::Value(Json::arrayValue); + auto& av = jv[jss::active] = Json::Value(Json::arrayValue); - for_each([&](std::shared_ptr&& sp) { + for_each([&](std::shared_ptr const& sp) { auto& pv = av.append(Json::Value(Json::objectValue)); pv[jss::public_key] = base64_encode(sp->getNodePublic().data(), sp->getNodePublic().size()); - pv[jss::type] = sp->slot()->inbound() ? "in" : "out"; + pv[jss::type] = sp->slot()->inbound() ? jss::in : jss::out; pv[jss::uptime] = static_cast(duration_cast(sp->uptime()).count()); if (sp->crawl()) { @@ -688,18 +700,20 @@ OverlayImpl::getOverlayInfo() } else { - pv[jss::port] = std::to_string(sp->getRemoteAddress().port()); + pv[jss::port] = sp->getRemoteAddress().port(); } } { auto version{sp->getVersion()}; if (!version.empty()) + { // Could move here if Json::value supported moving from strings pv[jss::version] = std::string{version}; + } } - std::uint32_t minSeq, maxSeq; + std::uint32_t minSeq = 0, maxSeq = 0; sp->ledgerRange(minSeq, maxSeq); if (minSeq != 0 || maxSeq != 0) pv[jss::complete_ledgers] = std::to_string(minSeq) + "-" + std::to_string(maxSeq); @@ -744,7 +758,7 @@ OverlayImpl::getServerCounts() Json::Value OverlayImpl::getUnlInfo() { - Json::Value validators = app_.validators().getJson(); + Json::Value validators = app_.getValidators().getJson(); if (validators.isMember(jss::publisher_lists)) { @@ -760,7 +774,7 @@ OverlayImpl::getUnlInfo() validators.removeMember(jss::trusted_validator_keys); validators.removeMember(jss::validation_quorum); - Json::Value validatorSites = app_.validatorSites().getJson(); + Json::Value validatorSites = app_.getValidatorSites().getJson(); if (validatorSites.isMember(jss::validator_sites)) { @@ -796,19 +810,19 @@ OverlayImpl::processCrawl(http_request_type const& req, Handoff& handoff) msg.insert("Connection", "close"); msg.body()["version"] = Json::Value(2u); - if (setup_.crawlOptions & CrawlOptions::Overlay) + if ((setup_.crawlOptions & CrawlOptions::Overlay) != 0u) { msg.body()["overlay"] = getOverlayInfo(); } - if (setup_.crawlOptions & CrawlOptions::ServerInfo) + if ((setup_.crawlOptions & CrawlOptions::ServerInfo) != 0u) { msg.body()["server"] = getServerInfo(); } - if (setup_.crawlOptions & CrawlOptions::ServerCounts) + if ((setup_.crawlOptions & CrawlOptions::ServerCounts) != 0u) { msg.body()["counts"] = getServerCounts(); } - if (setup_.crawlOptions & CrawlOptions::Unl) + if ((setup_.crawlOptions & CrawlOptions::Unl) != 0u) { msg.body()["unl"] = getUnlInfo(); } @@ -861,27 +875,25 @@ OverlayImpl::processValidatorList(http_request_type const& req, Handoff& handoff return fail(boost::beast::http::status::bad_request); // find the list - auto vl = app_.validators().getAvailable(key, version); + auto vl = app_.getValidators().getAvailable(key, version); if (!vl) { // 404 not found return fail(boost::beast::http::status::not_found); } - else if (!*vl) + if (!*vl) { return fail(boost::beast::http::status::bad_request); } - else - { - msg.result(boost::beast::http::status::ok); - msg.body() = *vl; + msg.result(boost::beast::http::status::ok); - msg.prepare_payload(); - handoff.response = std::make_shared(msg); - return true; - } + msg.body() = *vl; + + msg.prepare_payload(); + handoff.response = std::make_shared(msg); + return true; } bool @@ -903,40 +915,45 @@ OverlayImpl::processHealth(http_request_type const& req, Handoff& handoff) bool amendment_blocked = false; if (info.isMember(jss::amendment_blocked)) amendment_blocked = true; - int number_peers = info[jss::peers].asInt(); - std::string server_state = info[jss::server_state].asString(); + int const number_peers = info[jss::peers].asInt(); + std::string const server_state = info[jss::server_state].asString(); auto load_factor = info[jss::load_factor_server].asDouble() / info[jss::load_base].asDouble(); - enum { healthy, warning, critical }; - int health = healthy; - auto set_health = [&health](int state) { - if (health < state) - health = state; - }; + enum class HealthState { healthy, warning, critical }; + auto health = HealthState::healthy; + auto set_health = [&health](HealthState state) { health = std::max(health, state); }; msg.body()[jss::info] = Json::objectValue; if (last_validated_ledger_age >= 7 || last_validated_ledger_age < 0) { msg.body()[jss::info][jss::validated_ledger] = last_validated_ledger_age; if (last_validated_ledger_age < 20) - set_health(warning); + { + set_health(HealthState::warning); + } else - set_health(critical); + { + set_health(HealthState::critical); + } } if (amendment_blocked) { msg.body()[jss::info][jss::amendment_blocked] = true; - set_health(critical); + set_health(HealthState::critical); } if (number_peers <= 7) { msg.body()[jss::info][jss::peers] = number_peers; if (number_peers != 0) - set_health(warning); + { + set_health(HealthState::warning); + } else - set_health(critical); + { + set_health(HealthState::critical); + } } if (!(server_state == "full" || server_state == "validating" || server_state == "proposing")) @@ -944,30 +961,36 @@ OverlayImpl::processHealth(http_request_type const& req, Handoff& handoff) msg.body()[jss::info][jss::server_state] = server_state; if (server_state == "syncing" || server_state == "tracking" || server_state == "connected") { - set_health(warning); + set_health(HealthState::warning); } else - set_health(critical); + { + set_health(HealthState::critical); + } } if (load_factor > 100) { msg.body()[jss::info][jss::load_factor] = load_factor; if (load_factor < 1000) - set_health(warning); + { + set_health(HealthState::warning); + } else - set_health(critical); + { + set_health(HealthState::critical); + } } switch (health) { - case healthy: + case HealthState::healthy: msg.result(boost::beast::http::status::ok); break; - case warning: + case HealthState::warning: msg.result(boost::beast::http::status::service_unavailable); break; - case critical: + case HealthState::critical: msg.result(boost::beast::http::status::internal_server_error); break; } @@ -991,7 +1014,7 @@ OverlayImpl::getActivePeers() const Overlay::PeerSequence ret; ret.reserve(size()); - for_each([&ret](std::shared_ptr&& sp) { ret.emplace_back(std::move(sp)); }); + for_each([&ret](std::shared_ptr const& sp) { ret.emplace_back(sp); }); return ret; } @@ -1004,7 +1027,7 @@ OverlayImpl::getActivePeers( std::size_t& enabledInSkip) const { Overlay::PeerSequence ret; - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); active = ids_.size(); disabled = enabledInSkip = 0; @@ -1021,10 +1044,14 @@ OverlayImpl::getActivePeers( if (!reduceRelayEnabled) ++disabled; - if (toSkip.count(id) == 0) + if (!toSkip.contains(id)) + { ret.emplace_back(std::move(p)); + } else if (reduceRelayEnabled) + { ++enabledInSkip; + } } } @@ -1034,13 +1061,13 @@ OverlayImpl::getActivePeers( void OverlayImpl::checkTracking(std::uint32_t index) { - for_each([index](std::shared_ptr&& sp) { sp->checkTracking(index); }); + for_each([index](std::shared_ptr const& sp) { sp->checkTracking(index); }); } std::shared_ptr OverlayImpl::findPeerByShortID(Peer::id_t const& id) const { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto const iter = ids_.find(id); if (iter != ids_.end()) return iter->second.lock(); @@ -1052,7 +1079,7 @@ OverlayImpl::findPeerByShortID(Peer::id_t const& id) const std::shared_ptr OverlayImpl::findPeerByPublicKey(PublicKey const& pubKey) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); // NOTE The purpose of peer is to delay the destruction of PeerImp std::shared_ptr peer; for (auto const& e : ids_) @@ -1070,7 +1097,7 @@ void OverlayImpl::broadcast(protocol::TMProposeSet& m) { auto const sm = std::make_shared(m, protocol::mtPROPOSE_LEDGER); - for_each([&](std::shared_ptr&& p) { p->send(sm); }); + for_each([&](std::shared_ptr const& p) { p->send(sm); }); } std::set @@ -1079,8 +1106,8 @@ OverlayImpl::relay(protocol::TMProposeSet& m, uint256 const& uid, PublicKey cons if (auto const toSkip = app_.getHashRouter().shouldRelay(uid)) { auto const sm = std::make_shared(m, protocol::mtPROPOSE_LEDGER, validator); - for_each([&](std::shared_ptr&& p) { - if (toSkip->find(p->id()) == toSkip->end()) + for_each([&](std::shared_ptr const& p) { + if (!toSkip->contains(p->id())) p->send(sm); }); return *toSkip; @@ -1092,7 +1119,7 @@ void OverlayImpl::broadcast(protocol::TMValidation& m) { auto const sm = std::make_shared(m, protocol::mtVALIDATION); - for_each([sm](std::shared_ptr&& p) { p->send(sm); }); + for_each([sm](std::shared_ptr const& p) { p->send(sm); }); } std::set @@ -1101,8 +1128,8 @@ OverlayImpl::relay(protocol::TMValidation& m, uint256 const& uid, PublicKey cons if (auto const toSkip = app_.getHashRouter().shouldRelay(uid)) { auto const sm = std::make_shared(m, protocol::mtVALIDATION, validator); - for_each([&](std::shared_ptr&& p) { - if (toSkip->find(p->id()) == toSkip->end()) + for_each([&](std::shared_ptr const& p) { + if (!toSkip->contains(p->id())) p->send(sm); }); return *toSkip; @@ -1113,13 +1140,13 @@ OverlayImpl::relay(protocol::TMValidation& m, uint256 const& uid, PublicKey cons std::shared_ptr OverlayImpl::getManifestsMessage() { - std::lock_guard g(manifestLock_); + std::lock_guard const g(manifestLock_); - if (auto seq = app_.validatorManifests().sequence(); seq != manifestListSeq_) + if (auto seq = app_.getValidatorManifests().sequence(); seq != manifestListSeq_) { protocol::TMManifests tm; - app_.validatorManifests().for_each_manifest( + app_.getValidatorManifests().for_each_manifest( [&tm](std::size_t s) { tm.mutable_list()->Reserve(s); }, [&tm, &hr = app_.getHashRouter()](Manifest const& manifest) { tm.add_list()->set_stobject(manifest.serialized.data(), manifest.serialized.size()); @@ -1195,7 +1222,7 @@ OverlayImpl::relay( // 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; + ((total - minRelay) * app_.config().TX_RELAY_PERCENTAGE / 100); txMetrics_.addMetrics(enabledTarget, toSkip.size(), disabled); @@ -1232,7 +1259,7 @@ OverlayImpl::relay( void OverlayImpl::remove(Child& child) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); list_.erase(&child); if (list_.empty()) cond_.notify_all(); @@ -1251,7 +1278,7 @@ OverlayImpl::stopChildren() // won't be called until vector<> children leaves scope. std::vector> children; { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); if (!work_) return; work_ = std::nullopt; @@ -1274,7 +1301,7 @@ void OverlayImpl::autoConnect() { auto const result = m_peerFinder->autoconnect(); - for (auto addr : result) + for (auto const& addr : result) connect(addr); } @@ -1286,7 +1313,7 @@ OverlayImpl::sendEndpoints() { std::shared_ptr peer; { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto const iter = m_peers.find(e.first); if (iter != m_peers.end()) peer = iter->second.lock(); @@ -1297,7 +1324,7 @@ OverlayImpl::sendEndpoints() } void -OverlayImpl::sendTxQueue() +OverlayImpl::sendTxQueue() const { for_each([](auto const& p) { if (p->txReduceRelayEnabled()) @@ -1347,17 +1374,23 @@ OverlayImpl::updateSlotAndSquelch( return; if (!strand_.running_in_this_thread()) - return post( + { + post( strand_, // Must capture copies of reference parameters (i.e. key, validator) [this, key = key, validator = validator, peers = std::move(peers), type]() mutable { updateSlotAndSquelch(key, validator, std::move(peers), type); }); + return; + } + for (auto id : peers) + { slots_.updateSlotAndSquelch(key, validator, id, type, [&]() { reportInboundTraffic(TrafficCount::squelch_ignored, 0); }); + } } void @@ -1371,12 +1404,17 @@ OverlayImpl::updateSlotAndSquelch( return; if (!strand_.running_in_this_thread()) - return post( - strand_, - // Must capture copies of reference parameters (i.e. key, validator) - [this, key = key, validator = validator, peer, type]() { - updateSlotAndSquelch(key, validator, peer, type); - }); + { + { + post( + strand_, + // Must capture copies of reference parameters (i.e. key, validator) + [this, key = key, validator = validator, peer, type]() { + updateSlotAndSquelch(key, validator, peer, type); + }); + } + return; + } slots_.updateSlotAndSquelch(key, validator, peer, type, [&]() { reportInboundTraffic(TrafficCount::squelch_ignored, 0); @@ -1387,7 +1425,10 @@ void OverlayImpl::deletePeer(Peer::id_t id) { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&OverlayImpl::deletePeer, this, id)); + { + post(strand_, std::bind(&OverlayImpl::deletePeer, this, id)); + return; + } slots_.deletePeer(id, true); } @@ -1396,7 +1437,10 @@ void OverlayImpl::deleteIdlePeers() { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&OverlayImpl::deleteIdlePeers, this)); + { + post(strand_, std::bind(&OverlayImpl::deleteIdlePeers, this)); + return; + } slots_.deleteIdlePeers(); } diff --git a/src/xrpld/overlay/detail/OverlayImpl.h b/src/xrpld/overlay/detail/OverlayImpl.h index 9e891e6e8a..167d574188 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.h +++ b/src/xrpld/overlay/detail/OverlayImpl.h @@ -49,9 +49,8 @@ public: explicit Child(OverlayImpl& overlay); - virtual ~Child(); - public: + virtual ~Child(); virtual void stop() = 0; }; @@ -98,7 +97,7 @@ private: hash_map> ids_; Resolver& m_resolver; std::atomic next_id_; - int timer_count_; + int timer_count_{0}; std::atomic jqTransOverflow_{0}; std::atomic peerDisconnects_{0}; std::atomic peerDisconnectsCharges_{0}; @@ -255,7 +254,7 @@ public: { std::vector> wp; { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); // Iterate over a copy of the peer list because peer // destruction can invalidate iterators. @@ -429,7 +428,7 @@ private: http_request_type const& request, address_type remote_address); - std::shared_ptr + static std::shared_ptr makeErrorResponse( std::shared_ptr const& slot, http_request_type const& request, @@ -474,7 +473,7 @@ private: Controlled through the config section [crawl] overlay=[0|1] */ Json::Value - getOverlayInfo(); + getOverlayInfo() const; /** Returns information about the local server. Reported through the /crawl API @@ -522,7 +521,7 @@ private: /** Send once a second transactions' hashes aggregated by peers. */ void - sendTxQueue(); + sendTxQueue() const; /** Check if peers stopped relaying messages * and if slots stopped receiving messages from the validator */ @@ -573,7 +572,7 @@ private: collect_metrics() { auto counts = m_traffic.getCounts(); - std::lock_guard lock(m_statsMutex); + std::lock_guard const lock(m_statsMutex); XRPL_ASSERT( counts.size() == m_stats.trafficGauges.size(), "xrpl::OverlayImpl::collect_metrics : counts size do match"); diff --git a/src/xrpld/overlay/detail/PeerImp.cpp b/src/xrpld/overlay/detail/PeerImp.cpp index dc7c350697..13fe0c571c 100644 --- a/src/xrpld/overlay/detail/PeerImp.cpp +++ b/src/xrpld/overlay/detail/PeerImp.cpp @@ -65,8 +65,8 @@ PeerImp::PeerImp( , id_(id) , fingerprint_(getFingerprint(slot->remote_endpoint(), publicKey, to_string(id))) , prefix_(makePrefix(fingerprint_)) - , sink_(app_.journal("Peer"), prefix_) - , p_sink_(app_.journal("Protocol"), prefix_) + , sink_(app_.getJournal("Peer"), prefix_) + , p_sink_(app_.getJournal("Protocol"), prefix_) , journal_(sink_) , p_journal_(p_sink_) , stream_ptr_(std::move(stream_ptr)) @@ -83,7 +83,7 @@ PeerImp::PeerImp( , publicKey_(publicKey) , lastPingTime_(clock_type::now()) , creationTime_(clock_type::now()) - , squelch_(app_.journal("Squelch")) + , squelch_(app_.getJournal("Squelch")) , usage_(consumer) , fee_{Resource::feeTrivialPeer, ""} , slot_(slot) @@ -134,7 +134,10 @@ void PeerImp::run() { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&PeerImp::run, shared_from_this())); + { + post(strand_, std::bind(&PeerImp::run, shared_from_this())); + return; + } auto parseLedgerHash = [](std::string_view value) -> std::optional { if (uint256 ret; ret.parseHex(value)) @@ -169,7 +172,7 @@ PeerImp::run() fail("Malformed handshake data (3)"); { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); if (closed) closedLedgerHash_ = *closed; if (previous) @@ -177,9 +180,13 @@ PeerImp::run() } if (inbound_) + { doAccept(); + } else + { doProtocolStart(); + } // Anything else that needs to be done with the connection should be // done in doProtocolStart @@ -189,7 +196,10 @@ void PeerImp::stop() { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&PeerImp::stop, shared_from_this())); + { + post(strand_, std::bind(&PeerImp::stop, shared_from_this())); + return; + } if (!socket_.is_open()) return; @@ -209,14 +219,20 @@ void PeerImp::send(std::shared_ptr const& m) { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&PeerImp::send, shared_from_this(), m)); + { + post(strand_, std::bind(&PeerImp::send, shared_from_this(), m)); + return; + } if (!socket_.is_open()) return; // we are in progress of closing the connection if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } auto validator = m->getValidatorKey(); if (validator && !squelch_.expireSquelch(*validator)) @@ -273,7 +289,10 @@ void PeerImp::sendTxQueue() { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&PeerImp::sendTxQueue, shared_from_this())); + { + post(strand_, std::bind(&PeerImp::sendTxQueue, shared_from_this())); + return; + } if (!txQueue_.empty()) { @@ -291,7 +310,10 @@ void PeerImp::addTxQueue(uint256 const& hash) { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&PeerImp::addTxQueue, shared_from_this(), hash)); + { + post(strand_, std::bind(&PeerImp::addTxQueue, shared_from_this(), hash)); + return; + } if (txQueue_.size() == reduce_relay::MAX_TX_QUEUE_SIZE) { @@ -307,7 +329,10 @@ void PeerImp::removeTxQueue(uint256 const& hash) { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&PeerImp::removeTxQueue, shared_from_this(), hash)); + { + post(strand_, std::bind(&PeerImp::removeTxQueue, shared_from_this(), hash)); + return; + } auto removed = txQueue_.erase(hash); JLOG(p_journal_.trace()) << "removeTxQueue " << removed; @@ -339,7 +364,7 @@ PeerImp::crawl() const bool PeerImp::cluster() const { - return static_cast(app_.cluster().member(publicKey_)); + return static_cast(app_.getCluster().member(publicKey_)); } std::string @@ -366,8 +391,10 @@ PeerImp::json() ret[jss::cluster] = true; if (auto const n = name(); !n.empty()) + { // Could move here if Json::Value supported moving from a string ret[jss::name] = n; + } } if (auto const d = domain(); !d.empty()) @@ -384,7 +411,7 @@ PeerImp::json() ret[jss::protocol] = to_string(protocol_); { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); if (latency_) ret[jss::latency] = static_cast(latency_->count()); } @@ -392,7 +419,7 @@ PeerImp::json() ret[jss::uptime] = static_cast(std::chrono::duration_cast(uptime()).count()); - std::uint32_t minSeq, maxSeq; + std::uint32_t minSeq = 0, maxSeq = 0; ledgerRange(minSeq, maxSeq); if ((minSeq != 0) || (maxSeq != 0)) @@ -416,7 +443,7 @@ PeerImp::json() uint256 closedLedgerHash; protocol::TMStatusChange last_status; { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); closedLedgerHash = closedLedgerHash_; last_status = last_status_; } @@ -483,7 +510,7 @@ bool PeerImp::hasLedger(uint256 const& hash, std::uint32_t seq) const { { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); if ((seq != 0) && (seq >= minLedger_) && (seq <= maxLedger_) && (tracking_.load() == Tracking::converged)) return true; @@ -496,7 +523,7 @@ PeerImp::hasLedger(uint256 const& hash, std::uint32_t seq) const void PeerImp::ledgerRange(std::uint32_t& minSeq, std::uint32_t& maxSeq) const { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); minSeq = minLedger_; maxSeq = maxLedger_; @@ -505,7 +532,7 @@ PeerImp::ledgerRange(std::uint32_t& minSeq, std::uint32_t& maxSeq) const bool PeerImp::hasTxSet(uint256 const& hash) const { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); return std::find(recentTxSets_.begin(), recentTxSets_.end(), hash) != recentTxSets_.end(); } @@ -514,7 +541,7 @@ PeerImp::cycleStatus() { // Operations on closedLedgerHash_ and previousLedgerHash_ must be // guarded by recentLock_. - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); previousLedgerHash_ = closedLedgerHash_; closedLedgerHash_.zero(); } @@ -522,7 +549,7 @@ PeerImp::cycleStatus() bool PeerImp::hasRange(std::uint32_t uMin, std::uint32_t uMax) { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); return (tracking_ != Tracking::diverged) && (uMin >= minLedger_) && (uMax <= maxLedger_); } @@ -545,10 +572,13 @@ void PeerImp::fail(std::string const& reason) { if (!strand_.running_in_this_thread()) - return post( + { + post( strand_, std::bind( (void (Peer::*)(std::string const&))&PeerImp::fail, shared_from_this(), reason)); + return; + } if (!socket_.is_open()) return; @@ -611,7 +641,7 @@ PeerImp::onShutdown(error_code ec) // - 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 shouldLog = + 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); @@ -635,7 +665,7 @@ PeerImp::close() cancelTimer(); error_code ec; - socket_.close(ec); + socket_.close(ec); // NOLINT(bugprone-unused-return-value) overlay_.incPeerDisconnect(); @@ -658,7 +688,8 @@ PeerImp::setTimer(std::chrono::seconds interval) catch (std::exception const& ex) { JLOG(journal_.error()) << "setTimer: " << ex.what(); - return shutdown(); + shutdown(); + return; } timer_.async_wait(bind_executor( @@ -691,7 +722,8 @@ PeerImp::onTimer(error_code const& ec) // This should never happen JLOG(journal_.error()) << "onTimer: " << ec.message(); - return close(); + close(); + return; } // the timer expired before the shutdown completed @@ -699,18 +731,22 @@ PeerImp::onTimer(error_code const& ec) if (shutdown_) { JLOG(journal_.debug()) << "onTimer: shutdown timer expired"; - return close(); + close(); + return; } if (large_sendq_++ >= Tuning::sendqIntervals) - return fail("Large send queue"); + { + fail("Large send queue"); + return; + } if (auto const t = tracking_.load(); !inbound_ && t != Tracking::converged) { clock_type::duration duration; { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); duration = clock_type::now() - trackingTime_; } @@ -718,13 +754,17 @@ PeerImp::onTimer(error_code const& ec) (t == Tracking::unknown && (duration > app_.config().MAX_UNKNOWN_TIME))) { overlay_.peerFinder().on_failure(slot_); - return fail("Not useful"); + fail("Not useful"); + return; } } // Already waiting for PONG if (lastPingSeq_) - return fail("Ping Timeout"); + { + fail("Ping Timeout"); + return; + } lastPingTime_ = clock_type::now(); lastPingSeq_ = rand_int(); @@ -761,21 +801,27 @@ PeerImp::doAccept() // a shutdown was initiated before the handshake, there is nothing to do if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } auto const sharedValue = makeSharedValue(*stream_ptr_, journal_); // This shouldn't fail since we already computed // the shared value successfully in OverlayImpl if (!sharedValue) - return fail("makeSharedValue: Unexpected failure"); + { + fail("makeSharedValue: Unexpected failure"); + return; + } JLOG(journal_.debug()) << "Protocol: " << to_string(protocol_); - if (auto member = app_.cluster().member(publicKey_)) + if (auto member = app_.getCluster().member(publicKey_)) { { - std::unique_lock lock{nameMutex_}; + std::unique_lock const lock{nameMutex_}; name_ = *member; } JLOG(journal_.info()) << "Cluster name: " << *member; @@ -811,19 +857,29 @@ PeerImp::doAccept() if (!socket_.is_open()) return; if (ec == boost::asio::error::operation_aborted) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } if (ec) - return fail("onWriteResponse", ec); + { + fail("onWriteResponse", ec); + return; + } if (write_buffer->size() == bytes_transferred) - return doProtocolStart(); - return fail("Failed to write header"); + { + doProtocolStart(); + return; + } + fail("Failed to write header"); + return; })); } std::string PeerImp::name() const { - std::shared_lock read_lock{nameMutex_}; + std::shared_lock const read_lock{nameMutex_}; return name_; } @@ -842,14 +898,17 @@ PeerImp::doProtocolStart() { // a shutdown was initiated before the handshare, there is nothing to do if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } onReadMessage(error_code(), 0); // Send all the validator lists that have been loaded if (inbound_ && supportsFeature(ProtocolFeature::ValidatorListPropagation)) { - app_.validators().for_each_available( + app_.getValidators().for_each_available( [&](std::string const& manifest, std::uint32_t version, std::map const& blobInfos, @@ -895,17 +954,25 @@ PeerImp::onReadMessage(error_code ec, std::size_t bytes_transferred) if (ec == boost::asio::error::eof) { JLOG(journal_.debug()) << "EOF"; - return shutdown(); + shutdown(); + return; } if (ec == boost::asio::error::operation_aborted) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } - return fail("onReadMessage", ec); + fail("onReadMessage", ec); + return; } // we started shutdown, no reason to process further data if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } if (auto stream = journal_.trace()) { @@ -921,7 +988,7 @@ PeerImp::onReadMessage(error_code ec, std::size_t bytes_transferred) while (read_buffer_.size() > 0) { - std::size_t bytes_consumed; + std::size_t bytes_consumed = 0; using namespace std::chrono_literals; std::tie(bytes_consumed, ec) = perf::measureDurationAndLog( @@ -936,7 +1003,10 @@ PeerImp::onReadMessage(error_code ec, std::size_t bytes_transferred) // the error_code is produced by invokeProtocolMessage // it could be due to a bad message if (ec) - return fail("onReadMessage", ec); + { + fail("onReadMessage", ec); + return; + } if (bytes_consumed == 0) break; @@ -946,7 +1016,10 @@ PeerImp::onReadMessage(error_code ec, std::size_t bytes_transferred) // check if a shutdown was initiated while processing messages if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } readPending_ = true; @@ -978,9 +1051,13 @@ PeerImp::onWriteMessage(error_code ec, std::size_t bytes_transferred) if (ec) { if (ec == boost::asio::error::operation_aborted) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } - return fail("onWriteMessage", ec); + fail("onWriteMessage", ec); + return; } if (auto stream = journal_.trace()) @@ -995,7 +1072,10 @@ PeerImp::onWriteMessage(error_code ec, std::size_t bytes_transferred) send_queue_.pop(); if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } if (!send_queue_.empty()) { @@ -1003,7 +1083,7 @@ PeerImp::onWriteMessage(error_code ec, std::size_t bytes_transferred) XRPL_ASSERT(!shutdownStarted_, "xrpl::PeerImp::onWriteMessage : shutdown started"); // Timeout on writes only - return boost::asio::async_write( + boost::asio::async_write( stream_, boost::asio::buffer(send_queue_.front()->getBuffer(compressionEnabled_)), bind_executor( @@ -1013,6 +1093,7 @@ PeerImp::onWriteMessage(error_code ec, std::size_t bytes_transferred) shared_from_this(), std::placeholders::_1, std::placeholders::_2))); + return; } } @@ -1119,12 +1200,16 @@ PeerImp::onMessage(std::shared_ptr const& m) auto const rtt = std::chrono::round(clock_type::now() - lastPingTime_); - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); if (latency_) + { latency_ = (*latency_ * 7 + rtt) / 8; + } else + { latency_ = rtt; + } } return; @@ -1157,11 +1242,11 @@ PeerImp::onMessage(std::shared_ptr const& m) { auto const reportTime = NetClock::time_point{NetClock::duration{node.reporttime()}}; - app_.cluster().update(*publicKey, name, node.nodeload(), reportTime); + app_.getCluster().update(*publicKey, name, node.nodeload(), reportTime); } } - int loadSources = m->loadsources().size(); + int const loadSources = m->loadsources().size(); if (loadSources != 0) { Resource::Gossip gossip; @@ -1179,13 +1264,13 @@ PeerImp::onMessage(std::shared_ptr const& m) } // Calculate the cluster fee: - auto const thresh = app_.timeKeeper().now() - 90s; + auto const thresh = app_.getTimeKeeper().now() - 90s; std::uint32_t clusterFee = 0; std::vector fees; - fees.reserve(app_.cluster().size()); + fees.reserve(app_.getCluster().size()); - app_.cluster().for_each([&fees, thresh](ClusterNode const& status) { + app_.getCluster().for_each([&fees, thresh](ClusterNode const& status) { if (status.getReportTime() >= thresh) fees.push_back(status.getLoadFee()); }); @@ -1286,7 +1371,7 @@ PeerImp::handleTransaction( try { auto stx = std::make_shared(sit); - uint256 txID = stx->getTransactionID(); + uint256 const txID = stx->getTransactionID(); // Charge strongly for attempting to relay a txn with tfInnerBatchTxn // LCOV_EXCL_START @@ -1315,7 +1400,7 @@ PeerImp::handleTransaction( } // LCOV_EXCL_STOP - HashRouterFlags flags; + HashRouterFlags flags = HashRouterFlags::UNDEFINED; constexpr std::chrono::seconds tx_interval = 10s; if (!app_.getHashRouter().shouldProcess(txID, id_, flags, tx_interval)) @@ -1330,7 +1415,9 @@ PeerImp::handleTransaction( // Erase only if the server has seen this tx. If the server has not // seen this tx then the tx could not has been queued for this peer. else if (eraseTxQueue && txReduceRelayEnabled()) + { removeTxQueue(txID); + } overlay_.reportInboundTraffic( TrafficCount::category::transaction_duplicate, Message::messageSize(*m)); @@ -1402,7 +1489,10 @@ PeerImp::onMessage(std::shared_ptr const& m) // Verify ledger info type if (itype < protocol::liBASE || itype > protocol::liTS_CANDIDATE) - return badData("Invalid ledger info type"); + { + badData("Invalid ledger info type"); + return; + } auto const ltype = [&m]() -> std::optional<::protocol::TMLedgerType> { if (m->has_ltype()) @@ -1413,21 +1503,31 @@ PeerImp::onMessage(std::shared_ptr const& m) if (itype == protocol::liTS_CANDIDATE) { if (!m->has_ledgerhash()) - return badData("Invalid TX candidate set, missing TX set hash"); + { + badData("Invalid TX candidate set, missing TX set hash"); + return; + } } else if ( - !m->has_ledgerhash() && !m->has_ledgerseq() && !(ltype && *ltype == protocol::ltCLOSED)) + !m->has_ledgerhash() && !m->has_ledgerseq() && (!ltype || *ltype != protocol::ltCLOSED)) { - return badData("Invalid request"); + badData("Invalid request"); + return; } // Verify ledger type if (ltype && (*ltype < protocol::ltACCEPTED || *ltype > protocol::ltCLOSED)) - return badData("Invalid ledger type"); + { + badData("Invalid ledger type"); + return; + } // Verify ledger hash if (m->has_ledgerhash() && !stringIsUint256Sized(m->ledgerhash())) - return badData("Invalid ledger hash"); + { + badData("Invalid ledger hash"); + return; + } // Verify ledger sequence if (m->has_ledgerseq()) @@ -1439,7 +1539,8 @@ PeerImp::onMessage(std::shared_ptr const& m) if (app_.getLedgerMaster().getValidatedLedgerAge() <= 10s && ledgerSeq > app_.getLedgerMaster().getValidLedgerIndex() + 10) { - return badData("Invalid ledger sequence " + std::to_string(ledgerSeq)); + badData("Invalid ledger sequence " + std::to_string(ledgerSeq)); + return; } } @@ -1447,30 +1548,40 @@ PeerImp::onMessage(std::shared_ptr const& m) if (itype != protocol::liBASE) { if (m->nodeids_size() <= 0) - return badData("Invalid ledger node IDs"); + { + badData("Invalid ledger node IDs"); + return; + } for (auto const& nodeId : m->nodeids()) { if (deserializeSHAMapNodeID(nodeId) == std::nullopt) - return badData("Invalid SHAMap node ID"); + { + badData("Invalid SHAMap node ID"); + return; + } } } // Verify query type if (m->has_querytype() && m->querytype() != protocol::qtINDIRECT) - return badData("Invalid query type"); + { + badData("Invalid query type"); + return; + } // Verify query depth if (m->has_querydepth()) { if (m->querydepth() > Tuning::maxQueryDepth || itype == protocol::liBASE) { - return badData("Invalid query depth"); + badData("Invalid query depth"); + return; } } // Queue a job to process the request - std::weak_ptr weak = shared_from_this(); + std::weak_ptr const weak = shared_from_this(); app_.getJobQueue().addJob(jtLEDGER_REQ, "RcvGetLedger", [weak, m]() { if (auto peer = weak.lock()) peer->processLedgerRequest(m); @@ -1488,7 +1599,7 @@ PeerImp::onMessage(std::shared_ptr const& m) } fee_.update(Resource::feeModerateBurdenPeer, "received a proof path request"); - std::weak_ptr weak = shared_from_this(); + std::weak_ptr const weak = shared_from_this(); app_.getJobQueue().addJob(jtREPLAY_REQ, "RcvProofPReq", [weak, m]() { if (auto peer = weak.lock()) { @@ -1496,9 +1607,13 @@ PeerImp::onMessage(std::shared_ptr const& m) if (reply.has_error()) { if (reply.error() == protocol::TMReplyError::reBAD_REQUEST) + { peer->charge(Resource::feeMalformedRequest, "proof_path_request"); + } else + { peer->charge(Resource::feeRequestNoReply, "proof_path_request"); + } } else { @@ -1534,7 +1649,7 @@ PeerImp::onMessage(std::shared_ptr const& m) } fee_.fee = Resource::feeModerateBurdenPeer; - std::weak_ptr weak = shared_from_this(); + std::weak_ptr const weak = shared_from_this(); app_.getJobQueue().addJob(jtREPLAY_REQ, "RcvReplDReq", [weak, m]() { if (auto peer = weak.lock()) { @@ -1542,9 +1657,13 @@ PeerImp::onMessage(std::shared_ptr const& m) if (reply.has_error()) { if (reply.error() == protocol::TMReplyError::reBAD_REQUEST) + { peer->charge(Resource::feeMalformedRequest, "replay_delta_request"); + } else + { peer->charge(Resource::feeRequestNoReply, "replay_delta_request"); + } } else { @@ -1579,7 +1698,10 @@ PeerImp::onMessage(std::shared_ptr const& m) // Verify ledger hash if (!stringIsUint256Sized(m->ledgerhash())) - return badData("Invalid ledger hash"); + { + badData("Invalid ledger hash"); + return; + } // Verify ledger sequence { @@ -1588,7 +1710,8 @@ PeerImp::onMessage(std::shared_ptr const& m) { if (ledgerSeq != 0) { - return badData("Invalid ledger sequence " + std::to_string(ledgerSeq)); + badData("Invalid ledger sequence " + std::to_string(ledgerSeq)); + return; } } else @@ -1598,26 +1721,32 @@ PeerImp::onMessage(std::shared_ptr const& m) if (app_.getLedgerMaster().getValidatedLedgerAge() <= 10s && ledgerSeq > app_.getLedgerMaster().getValidLedgerIndex() + 10) { - return badData("Invalid ledger sequence " + std::to_string(ledgerSeq)); + badData("Invalid ledger sequence " + std::to_string(ledgerSeq)); + return; } } } // Verify ledger info type if (m->type() < protocol::liBASE || m->type() > protocol::liTS_CANDIDATE) - return badData("Invalid ledger info type"); + { + badData("Invalid ledger info type"); + return; + } // Verify reply error if (m->has_error() && (m->error() < protocol::reNO_LEDGER || m->error() > protocol::reBAD_REQUEST)) { - return badData("Invalid reply error"); + badData("Invalid reply error"); + return; } // Verify ledger nodes. if (m->nodes_size() <= 0 || m->nodes_size() > Tuning::hardMaxReplyNodes) { - return badData("Invalid Ledger/TXset nodes " + std::to_string(m->nodes_size())); + badData("Invalid Ledger/TXset nodes " + std::to_string(m->nodes_size())); + return; } // If there is a request cookie, attempt to relay the message @@ -1640,7 +1769,7 @@ PeerImp::onMessage(std::shared_ptr const& m) // Otherwise check if received data for a candidate transaction set if (m->type() == protocol::liTS_CANDIDATE) { - std::weak_ptr weak{shared_from_this()}; + std::weak_ptr const weak{shared_from_this()}; app_.getJobQueue().addJob(jtTXN_DATA, "RcvPeerData", [weak, ledgerHash, m]() { if (auto peer = weak.lock()) { @@ -1657,7 +1786,7 @@ PeerImp::onMessage(std::shared_ptr const& m) void PeerImp::onMessage(std::shared_ptr const& m) { - protocol::TMProposeSet& set = *m; + protocol::TMProposeSet const& set = *m; auto const sig = makeSlice(set.signature()); @@ -1682,7 +1811,7 @@ PeerImp::onMessage(std::shared_ptr const& m) // suppression for 30 seconds to avoid doing a relatively expensive lookup // every time a spam packet is received PublicKey const publicKey{makeSlice(set.nodepubkey())}; - auto const isTrusted = app_.validators().trusted(publicKey); + auto const isTrusted = app_.getValidators().trusted(publicKey); // If the operator has specified that untrusted proposals be dropped then // this happens here I.e. before further wasting CPU verifying the signature @@ -1748,10 +1877,10 @@ PeerImp::onMessage(std::shared_ptr const& m) set.proposeseq(), proposeHash, closeTime, - app_.timeKeeper().closeTime(), - calcNodeID(app_.validatorManifests().getMasterKey(publicKey))}); + app_.getTimeKeeper().closeTime(), + calcNodeID(app_.getValidatorManifests().getMasterKey(publicKey))}); - std::weak_ptr weak = shared_from_this(); + std::weak_ptr const weak = shared_from_this(); app_.getJobQueue().addJob( isTrusted ? jtPROPOSAL_t : jtPROPOSAL_ut, "checkPropose", [weak, isTrusted, m, proposal]() { if (auto peer = weak.lock()) @@ -1765,16 +1894,18 @@ PeerImp::onMessage(std::shared_ptr const& m) JLOG(p_journal_.trace()) << "Status: Change"; if (!m->has_networktime()) - m->set_networktime(app_.timeKeeper().now().time_since_epoch().count()); + m->set_networktime(app_.getTimeKeeper().now().time_since_epoch().count()); { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); if (!last_status_.has_newstatus() || m->has_newstatus()) + { last_status_ = *m; + } else { // preserve old status - protocol::NodeStatus status = last_status_.newstatus(); + protocol::NodeStatus const status = last_status_.newstatus(); last_status_ = *m; m->set_newstatus(status); } @@ -1786,7 +1917,7 @@ PeerImp::onMessage(std::shared_ptr const& m) { // Operations on closedLedgerHash_ and previousLedgerHash_ must be // guarded by recentLock_. - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); if (!closedLedgerHash_.isZero()) { outOfSync = true; @@ -1808,7 +1939,7 @@ PeerImp::onMessage(std::shared_ptr const& m) { // Operations on closedLedgerHash_ and previousLedgerHash_ must be // guarded by recentLock_. - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); if (peerChangedLedgers) { closedLedgerHash_ = m->ledgerhash(); @@ -1842,7 +1973,7 @@ PeerImp::onMessage(std::shared_ptr const& m) if (m->has_firstseq() && m->has_lastseq()) { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); minLedger_ = m->firstseq(); maxLedger_ = m->lastseq(); @@ -1856,7 +1987,7 @@ PeerImp::onMessage(std::shared_ptr const& m) checkTracking(m->ledgerseq(), app_.getLedgerMaster().getValidLedgerIndex()); } - app_.getOPs().pubPeerStatus([=, this]() -> Json::Value { + app_.getOPs().pubPeerStatus([m, this]() -> Json::Value { Json::Value j = Json::objectValue; if (m->has_newstatus()) @@ -1909,7 +2040,7 @@ PeerImp::onMessage(std::shared_ptr const& m) { uint256 closedLedgerHash{}; { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); closedLedgerHash = closedLedgerHash_; } j[jss::ledger_hash] = to_string(closedLedgerHash); @@ -1933,11 +2064,11 @@ PeerImp::onMessage(std::shared_ptr const& m) void PeerImp::checkTracking(std::uint32_t validationSeq) { - std::uint32_t serverSeq; + std::uint32_t serverSeq = 0; { // Extract the sequence number of the highest // ledger this peer has - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); serverSeq = maxLedger_; } @@ -1952,7 +2083,7 @@ PeerImp::checkTracking(std::uint32_t validationSeq) void PeerImp::checkTracking(std::uint32_t seq1, std::uint32_t seq2) { - int diff = std::max(seq1, seq2) - std::min(seq1, seq2); + int const diff = std::max(seq1, seq2) - std::min(seq1, seq2); if (diff < Tuning::convergedLedgerLimit) { @@ -1963,7 +2094,7 @@ PeerImp::checkTracking(std::uint32_t seq1, std::uint32_t seq2) if ((diff > Tuning::divergedLedgerLimit) && (tracking_.load() != Tracking::diverged)) { // The peer's ledger sequence is way off the validation's - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); tracking_ = Tracking::diverged; trackingTime_ = clock_type::now(); @@ -1983,7 +2114,7 @@ PeerImp::onMessage(std::shared_ptr const& m) if (m->status() == protocol::tsHAVE) { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); if (std::find(recentTxSets_.begin(), recentTxSets_.end(), hash) != recentTxSets_.end()) { @@ -2026,13 +2157,13 @@ PeerImp::onValidatorListMessage( return; } - auto const applyResult = app_.validators().applyListsAndBroadcast( + auto const applyResult = app_.getValidators().applyListsAndBroadcast( manifest, version, blobs, remote_address_.to_string(), hash, - app_.overlay(), + app_.getOverlay(), app_.getHashRouter(), app_.getOPs()); @@ -2050,7 +2181,7 @@ PeerImp::onValidatorListMessage( case ListDisposition::expired: // Future list case ListDisposition::pending: { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); XRPL_ASSERT( applyResult.publisherKey, @@ -2073,7 +2204,7 @@ PeerImp::onValidatorListMessage( case ListDisposition::known_sequence: #ifndef NDEBUG { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); XRPL_ASSERT( applyResult.sequence && applyResult.publisherKey, "xrpl::PeerImp::onValidatorListMessage : nonzero sequence " @@ -2227,7 +2358,7 @@ PeerImp::onMessage(std::shared_ptr const& m fee_.update(Resource::feeUselessData, "unsupported peer"); return; } - else if (m->version() < 2) + if (m->version() < 2) { JLOG(p_journal_.debug()) << "ValidatorListCollection: received invalid validator list " @@ -2259,7 +2390,7 @@ PeerImp::onMessage(std::shared_ptr const& m) try { - auto const closeTime = app_.timeKeeper().closeTime(); + auto const closeTime = app_.getTimeKeeper().closeTime(); std::shared_ptr val; { @@ -2267,7 +2398,7 @@ PeerImp::onMessage(std::shared_ptr const& m) val = std::make_shared( std::ref(sit), [this](PublicKey const& pk) { - return calcNodeID(app_.validatorManifests().getMasterKey(pk)); + return calcNodeID(app_.getValidatorManifests().getMasterKey(pk)); }, false); val->setSeen(closeTime); @@ -2275,7 +2406,7 @@ PeerImp::onMessage(std::shared_ptr const& m) if (!isCurrent( app_.getValidations().parms(), - app_.timeKeeper().closeTime(), + app_.getTimeKeeper().closeTime(), val->getSignTime(), val->getSeenTime())) { @@ -2287,7 +2418,7 @@ PeerImp::onMessage(std::shared_ptr const& m) // RH TODO: when isTrusted = false we should probably also cache a key // suppression for 30 seconds to avoid doing a relatively expensive // lookup every time a spam packet is received - auto const isTrusted = app_.validators().trusted(val->getSignerPublic()); + auto const isTrusted = app_.getValidators().trusted(val->getSignerPublic()); // If the operator has specified that untrusted validations be // dropped then this happens here I.e. before further wasting CPU @@ -2312,8 +2443,10 @@ PeerImp::onMessage(std::shared_ptr const& m) // peer receives within IDLED seconds since the message has been // relayed. if (relayed && (stopwatch().now() - *relayed) < reduce_relay::IDLED) + { overlay_.updateSlotAndSquelch( key, val->getSignerPublic(), id_, protocol::mtVALIDATION); + } // increase duplicate validations received overlay_.reportInboundTraffic( @@ -2331,7 +2464,7 @@ PeerImp::onMessage(std::shared_ptr const& m) { std::string const name = isTrusted ? "ChkTrust" : "ChkUntrust"; - std::weak_ptr weak = shared_from_this(); + std::weak_ptr const weak = shared_from_this(); app_.getJobQueue().addJob( isTrusted ? jtVALIDATION_t : jtVALIDATION_ut, name, [weak, val, m, key]() { if (auto peer = weak.lock()) @@ -2354,7 +2487,7 @@ PeerImp::onMessage(std::shared_ptr const& m) void PeerImp::onMessage(std::shared_ptr const& m) { - protocol::TMGetObjectByHash& packet = *m; + protocol::TMGetObjectByHash const& packet = *m; JLOG(p_journal_.trace()) << "received TMGetObjectByHash " << packet.type() << " " << packet.objects_size(); @@ -2383,7 +2516,7 @@ PeerImp::onMessage(std::shared_ptr const& m) return; } - std::weak_ptr weak = shared_from_this(); + std::weak_ptr const weak = shared_from_this(); app_.getJobQueue().addJob(jtREQUESTED_TXN, "DoTxs", [weak, m]() { if (auto peer = weak.lock()) peer->doTransactions(m); @@ -2422,7 +2555,7 @@ PeerImp::onMessage(std::shared_ptr const& m) uint256 const hash{obj.hash()}; // VFALCO TODO Move this someplace more sensible so we dont // need to inject the NodeStore interfaces. - std::uint32_t seq{obj.has_ledgerseq() ? obj.ledgerseq() : 0}; + std::uint32_t const seq{obj.has_ledgerseq() ? obj.ledgerseq() : 0}; auto nodeObject{app_.getNodeStore().fetchNodeObject(hash, seq)}; if (nodeObject) { @@ -2483,7 +2616,9 @@ PeerImp::onMessage(std::shared_ptr const& m) JLOG(p_journal_.debug()) << "GetObj: Late fetch pack for " << pLSeq; } else + { progress = true; + } } } @@ -2516,7 +2651,7 @@ PeerImp::onMessage(std::shared_ptr const& m) return; } - std::weak_ptr weak = shared_from_this(); + std::weak_ptr const weak = shared_from_this(); app_.getJobQueue().addJob(jtMISSING_TXN, "HandleHaveTxs", [weak, m]() { if (auto peer = weak.lock()) peer->handleHaveTransactions(m); @@ -2584,11 +2719,13 @@ PeerImp::onMessage(std::shared_ptr const& m) overlay_.addTxMetrics(m->transactions_size()); for (std::uint32_t i = 0; i < m->transactions_size(); ++i) + { handleTransaction( std::shared_ptr( m->mutable_transactions(i), [](protocol::TMTransaction*) {}), false, true); + } } void @@ -2596,7 +2733,10 @@ PeerImp::onMessage(std::shared_ptr const& m) { using on_message_fn = void (PeerImp::*)(std::shared_ptr const&); if (!strand_.running_in_this_thread()) - return post(strand_, std::bind((on_message_fn)&PeerImp::onMessage, shared_from_this(), m)); + { + post(strand_, std::bind((on_message_fn)&PeerImp::onMessage, shared_from_this(), m)); + return; + } if (!m->has_validatorpubkey()) { @@ -2610,7 +2750,7 @@ PeerImp::onMessage(std::shared_ptr const& m) fee_.update(Resource::feeInvalidData, "squelch bad pubkey"); return; } - PublicKey key(slice); + PublicKey const key(slice); // Ignore the squelch for validator's own messages. if (key == app_.getValidationPublicKey()) @@ -2619,11 +2759,15 @@ PeerImp::onMessage(std::shared_ptr const& m) return; } - std::uint32_t duration = m->has_squelchduration() ? m->squelchduration() : 0; + std::uint32_t const duration = m->has_squelchduration() ? m->squelchduration() : 0; if (!m->squelch()) + { squelch_.removeSquelch(key); + } else if (!squelch_.addSquelch(key, std::chrono::seconds{duration})) + { fee_.update(Resource::feeInvalidData, "squelch duration"); + } JLOG(p_journal_.debug()) << "onMessage: TMSquelch " << slice << " " << id() << " " << duration; } @@ -2668,7 +2812,7 @@ PeerImp::doFetchPack(std::shared_ptr const& packet) uint256 const hash{packet->ledgerhash()}; - std::weak_ptr weak = shared_from_this(); + std::weak_ptr const weak = shared_from_this(); auto elapsed = UptimeClock::now(); auto const pap = &app_; app_.getJobQueue().addJob(jtPACK, "MakeFetchPack", [pap, weak, packet, hash, elapsed]() { @@ -2719,7 +2863,7 @@ PeerImp::doTransactions(std::shared_ptr const& pack sttx->add(s); tx->set_rawtransaction(s.data(), s.size()); tx->set_status(txn->getStatus() == INCLUDED ? protocol::tsCURRENT : protocol::tsNEW); - tx->set_receivetimestamp(app_.timeKeeper().now().time_since_epoch().count()); + tx->set_receivetimestamp(app_.getTimeKeeper().now().time_since_epoch().count()); tx->set_deferred(txn->getSubmitResult().queued); } @@ -2797,7 +2941,7 @@ PeerImp::checkTransaction( { JLOG(p_journal_.debug()) << "Passing skipped pseudo pseudo-transaction tx " << tx->getID(); - app_.overlay().relay(tx->getID(), {}, *toSkip); + app_.getOverlay().relay(tx->getID(), {}, *toSkip); } if (!batch) { @@ -2873,18 +3017,22 @@ PeerImp::checkPropose( if (!cluster() && !peerPos.checkSign()) { - std::string desc{"Proposal fails sig check"}; + std::string const desc{"Proposal fails sig check"}; JLOG(p_journal_.warn()) << desc; charge(Resource::feeInvalidSignature, desc); return; } - bool relay; + bool relay = false; if (isTrusted) + { relay = app_.getOPs().processTrustedProposal(peerPos); + } else + { relay = app_.config().RELAY_UNTRUSTED_PROPOSALS == 1 || cluster(); + } if (relay) { @@ -2893,13 +3041,15 @@ PeerImp::checkPropose( // not be relayed to these peers. But the message must be counted // as part of the squelch logic. auto haveMessage = - app_.overlay().relay(*packet, peerPos.suppressionID(), peerPos.publicKey()); + app_.getOverlay().relay(*packet, peerPos.suppressionID(), peerPos.publicKey()); if (!haveMessage.empty()) + { overlay_.updateSlotAndSquelch( peerPos.suppressionID(), peerPos.publicKey(), std::move(haveMessage), protocol::mtPROPOSE_LEDGER); + } } } @@ -2911,7 +3061,7 @@ PeerImp::checkValidation( { if (!val->isValid()) { - std::string desc{"Validation forwarded by peer is invalid"}; + std::string const desc{"Validation forwarded by peer is invalid"}; JLOG(p_journal_.debug()) << desc; charge(Resource::feeInvalidSignature, desc); return; @@ -3226,7 +3376,7 @@ PeerImp::processLedgerRequest(std::shared_ptr const& m) } } - if (!map) + if (map == nullptr) { JLOG(p_journal_.warn()) << "processLedgerRequest: Unable to find map"; return; @@ -3235,7 +3385,8 @@ PeerImp::processLedgerRequest(std::shared_ptr const& m) // Add requested node data to reply if (m->nodeids_size() > 0) { - auto const queryDepth{m->has_querydepth() ? m->querydepth() : (isHighLatency() ? 2 : 1)}; + std::uint32_t const defaultDepth = isHighLatency() ? 2 : 1; + auto const queryDepth{m->has_querydepth() ? m->querydepth() : defaultDepth}; std::vector> data; @@ -3342,14 +3493,18 @@ PeerImp::getScore(bool haveItem) const std::optional latency; { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); latency = latency_; } if (latency) + { score -= latency->count() * spLatency; + } else + { score -= spNoLatency; + } return score; } @@ -3357,7 +3512,7 @@ PeerImp::getScore(bool haveItem) const bool PeerImp::isHighLatency() const { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); return latency_ >= peerHighLatency; } @@ -3365,7 +3520,7 @@ void PeerImp::Metrics::add_message(std::uint64_t bytes) { using namespace std::chrono_literals; - std::unique_lock lock{mutex_}; + std::unique_lock const lock{mutex_}; totalBytes_ += bytes; accumBytes_ += bytes; @@ -3388,14 +3543,14 @@ PeerImp::Metrics::add_message(std::uint64_t bytes) std::uint64_t PeerImp::Metrics::average_bytes() const { - std::shared_lock lock{mutex_}; + std::shared_lock const lock{mutex_}; return rollingAvgBytes_; } std::uint64_t PeerImp::Metrics::total_bytes() const { - std::shared_lock lock{mutex_}; + std::shared_lock const lock{mutex_}; return totalBytes_; } diff --git a/src/xrpld/overlay/detail/PeerImp.h b/src/xrpld/overlay/detail/PeerImp.h index b4393ebd62..7f7d8c9324 100644 --- a/src/xrpld/overlay/detail/PeerImp.h +++ b/src/xrpld/overlay/detail/PeerImp.h @@ -7,7 +7,6 @@ #include #include -#include #include #include #include @@ -409,7 +408,7 @@ public: return publicKey_; } - /** Return the version of rippled that the peer is running, if reported. */ + /** Return the version of xrpld that the peer is running, if reported. */ std::string getVersion() const; @@ -429,7 +428,7 @@ public: std::optional publisherListSequence(PublicKey const& pubKey) const override { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); auto iter = publisherListSequences_.find(pubKey); if (iter != publisherListSequences_.end()) @@ -440,7 +439,7 @@ public: void setPublisherListSequence(PublicKey const& pubKey, std::size_t const seq) override { - std::lock_guard sl(recentLock_); + std::lock_guard const sl(recentLock_); publisherListSequences_[pubKey] = seq; } @@ -815,8 +814,8 @@ PeerImp::PeerImp( , id_(id) , fingerprint_(getFingerprint(slot->remote_endpoint(), publicKey, to_string(id_))) , prefix_(makePrefix(fingerprint_)) - , sink_(app_.journal("Peer"), prefix_) - , p_sink_(app_.journal("Protocol"), prefix_) + , sink_(app_.getJournal("Peer"), prefix_) + , p_sink_(app_.getJournal("Protocol"), prefix_) , journal_(sink_) , p_journal_(p_sink_) , stream_ptr_(std::move(stream_ptr)) @@ -833,7 +832,7 @@ PeerImp::PeerImp( , publicKey_(publicKey) , lastPingTime_(clock_type::now()) , creationTime_(clock_type::now()) - , squelch_(app_.journal("Squelch")) + , squelch_(app_.getJournal("Squelch")) , usage_(usage) , fee_{Resource::feeTrivialPeer} , slot_(std::move(slot)) diff --git a/src/xrpld/overlay/detail/PeerReservationTable.cpp b/src/xrpld/overlay/detail/PeerReservationTable.cpp index 78f29ad155..6ea2253df9 100644 --- a/src/xrpld/overlay/detail/PeerReservationTable.cpp +++ b/src/xrpld/overlay/detail/PeerReservationTable.cpp @@ -30,7 +30,7 @@ PeerReservationTable::list() const -> std::vector { std::vector list; { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); list.reserve(table_.size()); std::copy(table_.begin(), table_.end(), std::back_inserter(list)); } @@ -38,7 +38,7 @@ PeerReservationTable::list() const -> std::vector return list; } -// See `ripple/app/main/DBInit.cpp` for the `CREATE TABLE` statement. +// See `include/xrpl/rdb/DBInit.h` for the `CREATE TABLE` statement. // It is unfortunate that we do not get to define a function for it. // We choose a `bool` return type to fit in with the error handling scheme @@ -47,7 +47,7 @@ PeerReservationTable::list() const -> std::vector bool PeerReservationTable::load(DatabaseCon& connection) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); connection_ = &connection; auto db = connection.checkoutDb(); @@ -62,7 +62,7 @@ PeerReservationTable::insert_or_assign(PeerReservation const& reservation) { std::optional previous; - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto hint = table_.find(reservation); if (hint != table_.end()) @@ -96,7 +96,7 @@ PeerReservationTable::erase(PublicKey const& nodeId) { std::optional previous; - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); auto const it = table_.find({nodeId}); if (it != table_.end()) diff --git a/src/xrpld/overlay/detail/PeerSet.cpp b/src/xrpld/overlay/detail/PeerSet.cpp index 1327d190c2..391fb6d3ca 100644 --- a/src/xrpld/overlay/detail/PeerSet.cpp +++ b/src/xrpld/overlay/detail/PeerSet.cpp @@ -37,7 +37,7 @@ private: std::set peers_; }; -PeerSetImpl::PeerSetImpl(Application& app) : app_(app), journal_(app.journal("PeerSet")) +PeerSetImpl::PeerSetImpl(Application& app) : app_(app), journal_(app.getJournal("PeerSet")) { } @@ -49,7 +49,7 @@ PeerSetImpl::addPeers( { using ScoredPeer = std::pair>; - auto const& overlay = app_.overlay(); + auto const& overlay = app_.getOverlay(); std::vector pairs; pairs.reserve(overlay.size()); @@ -90,7 +90,7 @@ PeerSetImpl::sendRequest( for (auto id : peers_) { - if (auto p = app_.overlay().findPeerByShortID(id)) + if (auto p = app_.getOverlay().findPeerByShortID(id)) p->send(packet); } } @@ -127,7 +127,7 @@ make_PeerSetBuilder(Application& app) class DummyPeerSet : public PeerSet { public: - DummyPeerSet(Application& app) : j_(app.journal("DummyPeerSet")) + DummyPeerSet(Application& app) : j_(app.getJournal("DummyPeerSet")) { } @@ -152,7 +152,7 @@ public: std::set const& getPeerIds() const override { - static std::set emptyPeers; + static std::set const emptyPeers; JLOG(j_.error()) << "DummyPeerSet getPeerIds should not be called"; return emptyPeers; } diff --git a/src/xrpld/overlay/detail/ProtocolMessage.h b/src/xrpld/overlay/detail/ProtocolMessage.h index 2442e48031..41d42674ba 100644 --- a/src/xrpld/overlay/detail/ProtocolMessage.h +++ b/src/xrpld/overlay/detail/ProtocolMessage.h @@ -237,7 +237,7 @@ template < std::shared_ptr parseMessageContent(MessageHeader const& header, Buffers const& buffers) { - auto const m = std::make_shared(); + auto m = std::make_shared(); ZeroCopyInputStream stream(buffers); stream.Skip(header.header_size); @@ -349,7 +349,7 @@ invokeProtocolMessage(Buffers const& buffers, Handler& handler, std::size_t& hin return result; } - bool success; + bool success = false; switch (header->message_type) { diff --git a/src/xrpld/overlay/detail/ProtocolVersion.cpp b/src/xrpld/overlay/detail/ProtocolVersion.cpp index 3163ba55e0..1a55030cd4 100644 --- a/src/xrpld/overlay/detail/ProtocolVersion.cpp +++ b/src/xrpld/overlay/detail/ProtocolVersion.cpp @@ -17,13 +17,10 @@ namespace xrpl { it may not contain any duplicates!) */ -// clang-format off -constexpr ProtocolVersion const supportedProtocolList[] -{ +constexpr ProtocolVersion const supportedProtocolList[]{ {2, 1}, - {2, 2} + {2, 2}, }; -// clang-format on // This ugly construct ensures that supportedProtocolList is sorted in strictly // ascending order and doesn't contain any duplicates. @@ -61,7 +58,7 @@ to_string(ProtocolVersion const& p) std::vector parseProtocolVersions(boost::beast::string_view const& value) { - static boost::regex re( + static boost::regex const re( "^" // start of line "XRPL/" // The string "XRPL/" "([2-9]|(?:[1-9][0-9]+))" // a number (greater than 2 with no leading @@ -81,8 +78,8 @@ parseProtocolVersions(boost::beast::string_view const& value) if (boost::regex_match(s, m, re)) { - std::uint16_t major; - std::uint16_t minor; + std::uint16_t major = 0; + std::uint16_t minor = 0; if (!beast::lexicalCastChecked(major, std::string(m[1]))) continue; @@ -115,9 +112,8 @@ negotiateProtocolVersion(std::vector const& versions) // output of std::set_intersection is sorted, that item is always going // to be the last one. So we get a little clever and avoid the need for // a container: - std::function pickVersion = [&result](ProtocolVersion const& v) { - result = v; - }; + std::function const pickVersion = + [&result](ProtocolVersion const& v) { result = v; }; std::set_intersection( std::begin(versions), diff --git a/src/xrpld/overlay/detail/TrafficCount.cpp b/src/xrpld/overlay/detail/TrafficCount.cpp index f782d7cf0a..2ce32b4468 100644 --- a/src/xrpld/overlay/detail/TrafficCount.cpp +++ b/src/xrpld/overlay/detail/TrafficCount.cpp @@ -36,16 +36,22 @@ TrafficCount::categorize( if (auto msg = dynamic_cast(&message)) { if (msg->type() == protocol::liTS_CANDIDATE) + { return (inbound && !msg->has_requestcookie()) ? TrafficCount::category::ld_tsc_get : TrafficCount::category::ld_tsc_share; + } if (msg->type() == protocol::liTX_NODE) + { return (inbound && !msg->has_requestcookie()) ? TrafficCount::category::ld_txn_get : TrafficCount::category::ld_txn_share; + } if (msg->type() == protocol::liAS_NODE) + { return (inbound && !msg->has_requestcookie()) ? TrafficCount::category::ld_asn_get : TrafficCount::category::ld_asn_share; + } return (inbound && !msg->has_requestcookie()) ? TrafficCount::category::ld_get : TrafficCount::category::ld_share; @@ -54,16 +60,22 @@ TrafficCount::categorize( if (auto msg = dynamic_cast(&message)) { if (msg->itype() == protocol::liTS_CANDIDATE) + { return (inbound || msg->has_requestcookie()) ? TrafficCount::category::gl_tsc_share : TrafficCount::category::gl_tsc_get; + } if (msg->itype() == protocol::liTX_NODE) + { return (inbound || msg->has_requestcookie()) ? TrafficCount::category::gl_txn_share : TrafficCount::category::gl_txn_get; + } if (msg->itype() == protocol::liAS_NODE) + { return (inbound || msg->has_requestcookie()) ? TrafficCount::category::gl_asn_share : TrafficCount::category::gl_asn_get; + } return (inbound || msg->has_requestcookie()) ? TrafficCount::category::gl_share : TrafficCount::category::gl_get; @@ -72,28 +84,40 @@ TrafficCount::categorize( if (auto msg = dynamic_cast(&message)) { if (msg->type() == protocol::TMGetObjectByHash::otLEDGER) + { return (msg->query() == inbound) ? TrafficCount::category::share_hash_ledger : TrafficCount::category::get_hash_ledger; + } if (msg->type() == protocol::TMGetObjectByHash::otTRANSACTION) + { return (msg->query() == inbound) ? TrafficCount::category::share_hash_tx : TrafficCount::category::get_hash_tx; + } if (msg->type() == protocol::TMGetObjectByHash::otTRANSACTION_NODE) + { return (msg->query() == inbound) ? TrafficCount::category::share_hash_txnode : TrafficCount::category::get_hash_txnode; + } if (msg->type() == protocol::TMGetObjectByHash::otSTATE_NODE) + { return (msg->query() == inbound) ? TrafficCount::category::share_hash_asnode : TrafficCount::category::get_hash_asnode; + } if (msg->type() == protocol::TMGetObjectByHash::otCAS_OBJECT) + { return (msg->query() == inbound) ? TrafficCount::category::share_cas_object : TrafficCount::category::get_cas_object; + } if (msg->type() == protocol::TMGetObjectByHash::otFETCH_PACK) + { return (msg->query() == inbound) ? TrafficCount::category::share_fetch_pack : TrafficCount::category::get_fetch_pack; + } if (msg->type() == protocol::TMGetObjectByHash::otTRANSACTIONS) return TrafficCount::category::get_transactions; diff --git a/src/xrpld/overlay/detail/TxMetrics.cpp b/src/xrpld/overlay/detail/TxMetrics.cpp index 72cd9d6fa2..ee0e42e5d6 100644 --- a/src/xrpld/overlay/detail/TxMetrics.cpp +++ b/src/xrpld/overlay/detail/TxMetrics.cpp @@ -12,7 +12,7 @@ void TxMetrics::addMetrics(protocol::MessageType type, std::uint32_t val) { auto add = [&](auto& m, std::uint32_t val) { - std::lock_guard lock(mutex); + std::lock_guard const lock(mutex); m.addMetrics(val); }; @@ -41,7 +41,7 @@ TxMetrics::addMetrics(protocol::MessageType type, std::uint32_t val) void TxMetrics::addMetrics(std::uint32_t selected, std::uint32_t suppressed, std::uint32_t notenabled) { - std::lock_guard lock(mutex); + std::lock_guard const lock(mutex); selectedPeers.addMetrics(selected); suppressedPeers.addMetrics(suppressed); notEnabled.addMetrics(notenabled); @@ -50,7 +50,7 @@ TxMetrics::addMetrics(std::uint32_t selected, std::uint32_t suppressed, std::uin void TxMetrics::addMetrics(std::uint32_t missing) { - std::lock_guard lock(mutex); + std::lock_guard const lock(mutex); missingTx.addMetrics(missing); } @@ -94,7 +94,7 @@ SingleMetrics::addMetrics(std::uint32_t val) Json::Value TxMetrics::json() const { - std::lock_guard l(mutex); + std::lock_guard const l(mutex); Json::Value ret(Json::objectValue); diff --git a/src/xrpld/peerfinder/PeerfinderManager.h b/src/xrpld/peerfinder/PeerfinderManager.h index ab53068fca..57fde8a569 100644 --- a/src/xrpld/peerfinder/PeerfinderManager.h +++ b/src/xrpld/peerfinder/PeerfinderManager.h @@ -8,6 +8,8 @@ #include +#include "xrpld/peerfinder/detail/Tuning.h" + #include namespace xrpl { @@ -27,7 +29,7 @@ struct Config This includes both inbound and outbound, but does not include fixed peers. */ - std::size_t maxPeers; + std::size_t maxPeers{Tuning::defaultMaxPeers}; /** The number of automatic outbound connections to maintain. Outbound connections are only maintained if autoConnect @@ -39,25 +41,25 @@ struct Config Inbound connections are only maintained if wantIncoming is `true`. */ - std::size_t inPeers; + std::size_t inPeers{0}; /** `true` if we want our IP address kept private. */ bool peerPrivate = true; /** `true` if we want to accept incoming connections. */ - bool wantIncoming; + bool wantIncoming{true}; /** `true` if we want to establish connections automatically */ - bool autoConnect; + bool autoConnect{true}; /** The listening port number. */ - std::uint16_t listeningPort; + std::uint16_t listeningPort{0}; /** The set of features we advertise. */ std::string features; /** Limit how many incoming connections we allow per IP */ - int ipLimit; + int ipLimit{0}; //-------------------------------------------------------------------------- @@ -74,7 +76,7 @@ struct Config /** Write the configuration into a property stream */ void - onWrite(beast::PropertyStream::Map& map); + onWrite(beast::PropertyStream::Map& map) const; /** Make PeerFinder::Config from configuration parameters * @param config server's configuration diff --git a/src/xrpld/peerfinder/README.md b/src/xrpld/peerfinder/README.md index 806984035b..e6eda747ce 100644 --- a/src/xrpld/peerfinder/README.md +++ b/src/xrpld/peerfinder/README.md @@ -2,8 +2,8 @@ ## Introduction -The _Ripple payment network_ consists of a collection of _peers_ running the -**rippled software**. Each peer maintains multiple outgoing connections and +The _XRPL payment network_ consists of a collection of _peers_ running the +**xrpld software**. Each peer maintains multiple outgoing connections and optional incoming connections to other peers. These connections are made over both the public Internet and private local area networks. This network defines a fully connected directed graph of nodes. Peers send and receive messages to @@ -175,7 +175,7 @@ When choosing addresses from the boot cache for the purpose of establishing outgoing connections, addresses are ranked in decreasing order of valence. The Bootcache is persistent. Entries are periodically inserted and updated in the corresponding SQLite database during program operation. When -**rippled** is launched, the existing Bootcache database data is accessed and +**xrpld** is launched, the existing Bootcache database data is accessed and loaded to accelerate the bootstrap process. Desirable entries in the Bootcache are addresses for servers which are known to @@ -306,7 +306,7 @@ the address, and its retry timer has expired. The PeerFinder makes its best effort to become fully connected to the fixed addresses specified in the configuration file before moving on to establish -outgoing connections to foreign peers. This security feature helps rippled +outgoing connections to foreign peers. This security feature helps xrpld establish itself with a trusted set of peers first before accepting untrusted data from the network. diff --git a/src/xrpld/peerfinder/detail/Bootcache.cpp b/src/xrpld/peerfinder/detail/Bootcache.cpp index af9a1ad57b..dac55d50d6 100644 --- a/src/xrpld/peerfinder/detail/Bootcache.cpp +++ b/src/xrpld/peerfinder/detail/Bootcache.cpp @@ -4,15 +4,14 @@ #include +#include + namespace xrpl { namespace PeerFinder { Bootcache::Bootcache(Store& store, clock_type& clock, beast::Journal journal) - : m_store(store) - , m_clock(clock) - , m_journal(journal) - , m_whenUpdate(m_clock.now()) - , m_needsUpdate(false) + : m_store(store), m_clock(clock), m_journal(journal), m_whenUpdate(m_clock.now()) + { } @@ -131,12 +130,11 @@ Bootcache::on_success(beast::IP::Endpoint const& endpoint) else { Entry entry(result.first->right); - if (entry.valence() < 0) - entry.valence() = 0; + entry.valence() = std::max(entry.valence(), 0); ++entry.valence(); m_map.erase(result.first); result = m_map.insert(value_type(endpoint, entry)); - XRPL_ASSERT(result.second, "ripple:PeerFinder::Bootcache::on_success : endpoint inserted"); + XRPL_ASSERT(result.second, "xrpl::PeerFinder::Bootcache::on_success : endpoint inserted"); } Entry const& entry(result.first->right); JLOG(m_journal.info()) << beast::leftw(18) << "Bootcache connect " << endpoint << " with " @@ -156,12 +154,11 @@ Bootcache::on_failure(beast::IP::Endpoint const& endpoint) else { Entry entry(result.first->right); - if (entry.valence() > 0) - entry.valence() = 0; + entry.valence() = std::min(entry.valence(), 0); --entry.valence(); m_map.erase(result.first); result = m_map.insert(value_type(endpoint, entry)); - XRPL_ASSERT(result.second, "ripple:PeerFinder::Bootcache::on_failure : endpoint inserted"); + XRPL_ASSERT(result.second, "xrpl::PeerFinder::Bootcache::on_failure : endpoint inserted"); } Entry const& entry(result.first->right); auto const n(std::abs(entry.valence())); diff --git a/src/xrpld/peerfinder/detail/Bootcache.h b/src/xrpld/peerfinder/detail/Bootcache.h index a4687d95f4..9ab0a878e8 100644 --- a/src/xrpld/peerfinder/detail/Bootcache.h +++ b/src/xrpld/peerfinder/detail/Bootcache.h @@ -97,7 +97,7 @@ private: clock_type::time_point m_whenUpdate; // Set to true when a database update is needed - bool m_needsUpdate; + bool m_needsUpdate{false}; public: static constexpr int staticValence = 32; diff --git a/src/xrpld/peerfinder/detail/Checker.h b/src/xrpld/peerfinder/detail/Checker.h index 3e67754db9..21cc0f160e 100644 --- a/src/xrpld/peerfinder/detail/Checker.h +++ b/src/xrpld/peerfinder/detail/Checker.h @@ -44,7 +44,7 @@ private: async_op(Checker& owner, boost::asio::io_context& io_context, Handler&& handler); - ~async_op(); + virtual ~async_op(); void stop() override; @@ -108,7 +108,7 @@ template Checker::async_op::async_op( Checker& owner, boost::asio::io_context& io_context, - Handler&& handler) + Handler&& handler) // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) : checker_(owner), socket_(io_context), handler_(std::forward(handler)) { } @@ -154,7 +154,7 @@ template void Checker::stop() { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); if (!stop_) { stop_ = true; @@ -180,7 +180,7 @@ Checker::async_connect(beast::IP::Endpoint const& endpoint, Handler&& auto const op = std::make_shared>(*this, io_context_, std::forward(handler)); { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); list_.push_back(*op); } op->socket_.async_connect( @@ -192,7 +192,7 @@ template void Checker::remove(basic_async_op& op) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); list_.erase(list_.iterator_to(op)); if (list_.size() == 0) cond_.notify_all(); diff --git a/src/xrpld/peerfinder/detail/Counts.h b/src/xrpld/peerfinder/detail/Counts.h index 26e02a4cb1..8d40b44300 100644 --- a/src/xrpld/peerfinder/detail/Counts.h +++ b/src/xrpld/peerfinder/detail/Counts.h @@ -13,24 +13,6 @@ namespace PeerFinder { class Counts { public: - Counts() - : m_attempts(0) - , m_active(0) - , m_in_max(0) - , m_in_active(0) - , m_out_max(0) - , m_out_active(0) - , m_fixed(0) - , m_fixed_active(0) - , m_reserved(0) - - , m_acceptCount(0) - , m_closingCount(0) - { - } - - //-------------------------------------------------------------------------- - /** Adds the slot state and properties to the slot counts. */ void add(Slot const& s) @@ -282,38 +264,38 @@ private: private: /** Outbound connection attempts. */ - int m_attempts; + int m_attempts{0}; /** Active connections, including fixed and reserved. */ - std::size_t m_active; + std::size_t m_active{0}; /** Total number of inbound slots. */ - std::size_t m_in_max; + std::size_t m_in_max{0}; /** Number of inbound slots assigned to active peers. */ - std::size_t m_in_active; + std::size_t m_in_active{0}; /** Maximum desired outbound slots. */ - std::size_t m_out_max; + std::size_t m_out_max{0}; /** Active outbound slots. */ - std::size_t m_out_active; + std::size_t m_out_active{0}; /** Fixed connections. */ - std::size_t m_fixed; + std::size_t m_fixed{0}; /** Active fixed connections. */ - std::size_t m_fixed_active; + std::size_t m_fixed_active{0}; /** Reserved connections. */ - std::size_t m_reserved; + std::size_t m_reserved{0}; // Number of inbound connections that are // not active or gracefully closing. - int m_acceptCount; + int m_acceptCount{0}; // Number of connections that are gracefully closing. - int m_closingCount; + int m_closingCount{0}; }; } // namespace PeerFinder diff --git a/src/xrpld/peerfinder/detail/Fixed.h b/src/xrpld/peerfinder/detail/Fixed.h index 75b5ed9062..8b67347e6a 100644 --- a/src/xrpld/peerfinder/detail/Fixed.h +++ b/src/xrpld/peerfinder/detail/Fixed.h @@ -9,7 +9,7 @@ namespace PeerFinder { class Fixed { public: - explicit Fixed(clock_type& clock) : m_when(clock.now()), m_failures(0) + explicit Fixed(clock_type& clock) : m_when(clock.now()) { } @@ -40,7 +40,7 @@ public: private: clock_type::time_point m_when; - std::size_t m_failures; + std::size_t m_failures{0}; }; } // namespace PeerFinder diff --git a/src/xrpld/peerfinder/detail/Livecache.h b/src/xrpld/peerfinder/detail/Livecache.h index bedfd6c9d6..ac435e1e24 100644 --- a/src/xrpld/peerfinder/detail/Livecache.h +++ b/src/xrpld/peerfinder/detail/Livecache.h @@ -327,7 +327,7 @@ public: friend class Livecache; lists_type m_lists; - Histogram m_hist; + Histogram m_hist{}; } hops; /** Returns `true` if the cache is empty. */ diff --git a/src/xrpld/peerfinder/detail/Logic.h b/src/xrpld/peerfinder/detail/Logic.h index 39f1ca6ae6..ad0a2f8b96 100644 --- a/src/xrpld/peerfinder/detail/Logic.h +++ b/src/xrpld/peerfinder/detail/Logic.h @@ -106,7 +106,7 @@ public: void load() { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); bootcache_.load(); } @@ -119,7 +119,7 @@ public: void stop() { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); stopping_ = true; if (fetchSource_ != nullptr) fetchSource_->cancel(); @@ -134,7 +134,7 @@ public: void config(Config const& c) { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); config_ = c; counts_.onConfig(config_); } @@ -142,7 +142,7 @@ public: Config config() { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); return config_; } @@ -155,7 +155,7 @@ public: void addFixedPeer(std::string const& name, std::vector const& addresses) { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); if (addresses.empty()) { @@ -197,7 +197,7 @@ public: if (ec == boost::asio::error::operation_aborted) return; - std::lock_guard _(lock_); + std::lock_guard const _(lock_); auto const iter(slots_.find(remoteAddress)); if (iter == slots_.end()) { @@ -212,7 +212,7 @@ public: slot.connectivityCheckInProgress = false; beast::WrappedSink sink{m_journal.sink(), slot.prefix()}; - beast::Journal journal{sink}; + beast::Journal const journal{sink}; if (ec) { @@ -239,7 +239,7 @@ public: JLOG(m_journal.debug()) << beast::leftw(18) << "Logic accept" << remote_endpoint << " on local " << local_endpoint; - std::lock_guard _(lock_); + std::lock_guard const _(lock_); // Check for connection limit per address if (is_public(remote_endpoint)) @@ -287,7 +287,7 @@ public: { JLOG(m_journal.debug()) << beast::leftw(18) << "Logic connect " << remote_endpoint; - std::lock_guard _(lock_); + std::lock_guard const _(lock_); // Check for duplicate connection if (slots_.find(remote_endpoint) != slots_.end()) @@ -322,11 +322,11 @@ public: onConnected(SlotImp::ptr const& slot, beast::IP::Endpoint const& local_endpoint) { beast::WrappedSink sink{m_journal.sink(), slot->prefix()}; - beast::Journal journal{sink}; + beast::Journal const journal{sink}; JLOG(journal.trace()) << "Logic connected on local " << local_endpoint; - std::lock_guard _(lock_); + std::lock_guard const _(lock_); // The object must exist in our table XRPL_ASSERT( @@ -360,12 +360,12 @@ public: activate(SlotImp::ptr const& slot, PublicKey const& key, bool reserved) { beast::WrappedSink sink{m_journal.sink(), slot->prefix()}; - beast::Journal journal{sink}; + beast::Journal const journal{sink}; JLOG(journal.debug()) << "Logic handshake " << slot->remote_endpoint() << " with " << (reserved ? "reserved " : "") << "key " << key; - std::lock_guard _(lock_); + std::lock_guard const _(lock_); // The object must exist in our table XRPL_ASSERT( @@ -436,7 +436,7 @@ public: std::vector redirect(SlotImp::ptr const& slot) { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); RedirectHandouts h(slot); livecache_.hops.shuffle(); handout(&h, (&h) + 1, livecache_.hops.begin(), livecache_.hops.end()); @@ -452,9 +452,9 @@ public: std::vector autoconnect() { - std::vector const none; + std::vector none; - std::lock_guard _(lock_); + std::lock_guard const _(lock_); // Count how many more outbound attempts to make // @@ -560,7 +560,7 @@ public: { std::vector, std::vector>> result; - std::lock_guard _(lock_); + std::lock_guard const _(lock_); clock_type::time_point const now = m_clock.now(); if (m_whenBroadcast <= now) @@ -624,7 +624,7 @@ public: SlotImp::ptr const& slot = t.slot(); auto const& list = t.list(); beast::WrappedSink sink{m_journal.sink(), slot->prefix()}; - beast::Journal journal{sink}; + beast::Journal const journal{sink}; JLOG(journal.trace()) << "Logic sending " << list.size() << ((list.size() == 1) ? " endpoint" : " endpoints"); result.push_back(std::make_pair(slot, list)); @@ -639,7 +639,7 @@ public: void once_per_second() { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); // Expire the Livecache livecache_.expire(); @@ -725,7 +725,7 @@ public: on_endpoints(SlotImp::ptr const& slot, Endpoints list) { beast::WrappedSink sink{m_journal.sink(), slot->prefix()}; - beast::Journal journal{sink}; + beast::Journal const journal{sink}; // If we're sent too many endpoints, sample them at random: if (list.size() > Tuning::numberOfEndpointsMax) @@ -737,7 +737,7 @@ public: JLOG(journal.trace()) << "Endpoints contained " << list.size() << ((list.size() > 1) ? " entries" : " entry"); - std::lock_guard _(lock_); + std::lock_guard const _(lock_); // The object must exist in our table XRPL_ASSERT( @@ -863,12 +863,12 @@ public: void on_closed(SlotImp::ptr const& slot) { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); remove(slot); beast::WrappedSink sink{m_journal.sink(), slot->prefix()}; - beast::Journal journal{sink}; + beast::Journal const journal{sink}; // Mark fixed slot failure if (slot->fixed() && !slot->inbound() && slot->state() != Slot::active) @@ -921,7 +921,7 @@ public: void on_failure(SlotImp::ptr const& slot) { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); bootcache_.on_failure(slot->remote_endpoint()); } @@ -1010,8 +1010,8 @@ public: addBootcacheAddresses(IPAddresses const& list) { int count(0); - std::lock_guard _(lock_); - for (auto addr : list) + std::lock_guard const _(lock_); + for (auto const& addr : list) { if (bootcache_.insertStatic(addr)) ++count; @@ -1027,7 +1027,7 @@ public: { { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); if (stopping_) return; fetchSource_ = source; @@ -1039,7 +1039,7 @@ public: source->fetch(results, m_journal); { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); if (stopping_) return; fetchSource_ = nullptr; @@ -1110,7 +1110,7 @@ public: void onWrite(beast::PropertyStream::Map& map) { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); // VFALCO NOTE These ugly casts are needed because // of how std::size_t is declared on some linuxes @@ -1188,7 +1188,7 @@ Logic::onRedirects( FwdIter last, boost::asio::ip::tcp::endpoint const& remote_address) { - std::lock_guard _(lock_); + std::lock_guard const _(lock_); std::size_t n = 0; for (; first != last && n < Tuning::maxRedirects; ++first, ++n) bootcache_.insert(beast::IPAddressConversion::from_asio(*first)); diff --git a/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp b/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp index 58246cd198..6a158fbbab 100644 --- a/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp +++ b/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp @@ -1,17 +1,13 @@ #include #include +#include + namespace xrpl { namespace PeerFinder { -Config::Config() - : maxPeers(Tuning::defaultMaxPeers) - , outPeers(calcOutPeers()) - , inPeers(0) - , wantIncoming(true) - , autoConnect(true) - , listeningPort(0) - , ipLimit(0) +Config::Config() : outPeers(calcOutPeers()) + { } @@ -21,7 +17,7 @@ operator==(Config const& lhs, Config const& rhs) return lhs.autoConnect == rhs.autoConnect && lhs.peerPrivate == rhs.peerPrivate && lhs.wantIncoming == rhs.wantIncoming && lhs.inPeers == rhs.inPeers && lhs.maxPeers == rhs.maxPeers && lhs.outPeers == rhs.outPeers && - lhs.features == lhs.features && lhs.ipLimit == rhs.ipLimit && + lhs.features == rhs.features && lhs.ipLimit == rhs.ipLimit && lhs.listeningPort == rhs.listeningPort; } @@ -51,7 +47,7 @@ Config::applyTuning() } void -Config::onWrite(beast::PropertyStream::Map& map) +Config::onWrite(beast::PropertyStream::Map& map) const { map["max_peers"] = maxPeers; map["out_peers"] = outPeers; @@ -76,13 +72,12 @@ Config::makeConfig( // Servers with peer privacy don't want to allow incoming connections config.wantIncoming = (!config.peerPrivate) && (port != 0); - if (!cfg.PEERS_OUT_MAX && !cfg.PEERS_IN_MAX) + if ((cfg.PEERS_OUT_MAX == 0u) && (cfg.PEERS_IN_MAX == 0u)) { if (cfg.PEERS_MAX != 0) config.maxPeers = cfg.PEERS_MAX; - if (config.maxPeers < Tuning::minOutCount) - config.maxPeers = Tuning::minOutCount; + config.maxPeers = std::max(config.maxPeers, Tuning::minOutCount); config.outPeers = config.calcOutPeers(); // Calculate the number of outbound peers we want. If we dont want @@ -93,9 +88,13 @@ Config::makeConfig( // Calculate the largest number of inbound connections we could // take. if (config.maxPeers >= config.outPeers) + { config.inPeers = config.maxPeers - config.outPeers; + } else + { config.inPeers = 0; + } } else { diff --git a/src/xrpld/peerfinder/detail/PeerfinderManager.cpp b/src/xrpld/peerfinder/detail/PeerfinderManager.cpp index d04e09441c..e9c42b7eb5 100644 --- a/src/xrpld/peerfinder/detail/PeerfinderManager.cpp +++ b/src/xrpld/peerfinder/detail/PeerfinderManager.cpp @@ -33,8 +33,7 @@ public: beast::Journal journal, BasicConfig const& config, beast::insight::Collector::ptr const& collector) - : Manager() - , io_context_(io_context) + : io_context_(io_context) , work_(std::in_place, boost::asio::make_work_guard(io_context_)) , m_clock(clock) , m_journal(journal) @@ -118,21 +117,21 @@ public: void on_endpoints(std::shared_ptr const& slot, Endpoints const& endpoints) override { - SlotImp::ptr impl(std::dynamic_pointer_cast(slot)); + SlotImp::ptr const impl(std::dynamic_pointer_cast(slot)); m_logic.on_endpoints(impl, endpoints); } void on_closed(std::shared_ptr const& slot) override { - SlotImp::ptr impl(std::dynamic_pointer_cast(slot)); + SlotImp::ptr const impl(std::dynamic_pointer_cast(slot)); m_logic.on_closed(impl); } void on_failure(std::shared_ptr const& slot) override { - SlotImp::ptr impl(std::dynamic_pointer_cast(slot)); + SlotImp::ptr const impl(std::dynamic_pointer_cast(slot)); m_logic.on_failure(impl); } @@ -150,21 +149,21 @@ public: onConnected(std::shared_ptr const& slot, beast::IP::Endpoint const& local_endpoint) override { - SlotImp::ptr impl(std::dynamic_pointer_cast(slot)); + SlotImp::ptr const impl(std::dynamic_pointer_cast(slot)); return m_logic.onConnected(impl, local_endpoint); } Result activate(std::shared_ptr const& slot, PublicKey const& key, bool reserved) override { - SlotImp::ptr impl(std::dynamic_pointer_cast(slot)); + SlotImp::ptr const impl(std::dynamic_pointer_cast(slot)); return m_logic.activate(impl, key, reserved); } std::vector redirect(std::shared_ptr const& slot) override { - SlotImp::ptr impl(std::dynamic_pointer_cast(slot)); + SlotImp::ptr const impl(std::dynamic_pointer_cast(slot)); return m_logic.redirect(impl); } @@ -227,7 +226,7 @@ private: void collect_metrics() { - std::lock_guard lock(m_statsMutex); + std::lock_guard const lock(m_statsMutex); m_stats.activeInboundPeers = m_logic.counts_.inboundActive(); m_stats.activeOutboundPeers = m_logic.counts_.out_active(); } diff --git a/src/xrpld/peerfinder/detail/Store.h b/src/xrpld/peerfinder/detail/Store.h index dab50b3ce0..390f80800a 100644 --- a/src/xrpld/peerfinder/detail/Store.h +++ b/src/xrpld/peerfinder/detail/Store.h @@ -22,7 +22,7 @@ public: explicit Entry() = default; beast::IP::Endpoint endpoint; - int valence; + int valence{}; }; virtual void save(std::vector const& v) = 0; diff --git a/src/xrpld/perflog/detail/PerfLogImp.cpp b/src/xrpld/perflog/detail/PerfLogImp.cpp index d1267953e4..266c99a147 100644 --- a/src/xrpld/perflog/detail/PerfLogImp.cpp +++ b/src/xrpld/perflog/detail/PerfLogImp.cpp @@ -69,9 +69,9 @@ PerfLogImp::Counters::countersJson() const { Rpc value; { - std::lock_guard lock(proc.second.mutex); - if (!proc.second.value.started && !proc.second.value.finished && - !proc.second.value.errored) + std::lock_guard const lock(proc.second.mutex); + if ((proc.second.value.started == 0u) && (proc.second.value.finished == 0u) && + (proc.second.value.errored == 0u)) { continue; } @@ -90,7 +90,7 @@ PerfLogImp::Counters::countersJson() const rpcobj[proc.first] = p; } - if (totalRpc.started) + if (totalRpc.started != 0u) { Json::Value totalRpcJson(Json::objectValue); totalRpcJson[jss::started] = std::to_string(totalRpc.started); @@ -107,9 +107,9 @@ PerfLogImp::Counters::countersJson() const { Jq value; { - std::lock_guard lock(proc.second.mutex); - if (!proc.second.value.queued && !proc.second.value.started && - !proc.second.value.finished) + std::lock_guard const lock(proc.second.mutex); + if ((proc.second.value.queued == 0u) && (proc.second.value.started == 0u) && + (proc.second.value.finished == 0u)) { continue; } @@ -130,7 +130,7 @@ PerfLogImp::Counters::countersJson() const jobQueueObj[JobTypes::name(proc.first)] = j; } - if (totalJq.queued) + if (totalJq.queued != 0u) { Json::Value totalJqJson(Json::objectValue); totalJqJson[jss::queued] = std::to_string(totalJq.queued); @@ -156,7 +156,7 @@ PerfLogImp::Counters::currentJson() const Json::Value jobsArray(Json::arrayValue); auto const jobs = [this] { - std::lock_guard lock(jobsMutex_); + std::lock_guard const lock(jobsMutex_); return jobs_; }(); @@ -174,7 +174,7 @@ PerfLogImp::Counters::currentJson() const Json::Value methodsArray(Json::arrayValue); std::vector methods; { - std::lock_guard lock(methodsMutex_); + std::lock_guard const lock(methodsMutex_); methods.reserve(methods_.size()); for (auto const& m : methods_) methods.push_back(m.second); @@ -257,8 +257,10 @@ void PerfLogImp::report() { if (!logFile_) + { // If logFile_ is not writable do no further work. return; + } auto const present = system_clock::now(); if (present < lastLog_ + setup_.logInterval) @@ -268,7 +270,7 @@ PerfLogImp::report() Json::Value report(Json::objectValue); report[jss::time] = to_string(std::chrono::floor(present)); { - std::lock_guard lock{counters_.jobsMutex_}; + std::lock_guard const lock{counters_.jobsMutex_}; report[jss::workers] = static_cast(counters_.jobs_.size()); } report[jss::hostid] = hostname_; @@ -309,10 +311,10 @@ PerfLogImp::rpcStart(std::string const& method, std::uint64_t const requestId) } { - std::lock_guard lock(counter->second.mutex); + std::lock_guard const lock(counter->second.mutex); ++counter->second.value.started; } - std::lock_guard lock(counters_.methodsMutex_); + std::lock_guard const lock(counters_.methodsMutex_); counters_.methods_[requestId] = {counter->first.c_str(), steady_clock::now()}; } @@ -329,7 +331,7 @@ PerfLogImp::rpcEnd(std::string const& method, std::uint64_t const requestId, boo } steady_time_point startTime; { - std::lock_guard lock(counters_.methodsMutex_); + std::lock_guard const lock(counters_.methodsMutex_); auto const e = counters_.methods_.find(requestId); if (e != counters_.methods_.end()) { @@ -343,11 +345,15 @@ PerfLogImp::rpcEnd(std::string const& method, std::uint64_t const requestId, boo // LCOV_EXCL_STOP } } - std::lock_guard lock(counter->second.mutex); + std::lock_guard const lock(counter->second.mutex); if (finish) + { ++counter->second.value.finished; + } else + { ++counter->second.value.errored; + } counter->second.value.duration += std::chrono::duration_cast(steady_clock::now() - startTime); } @@ -363,7 +369,7 @@ PerfLogImp::jobQueue(JobType const type) return; // LCOV_EXCL_STOP } - std::lock_guard lock(counter->second.mutex); + std::lock_guard const lock(counter->second.mutex); ++counter->second.value.queued; } @@ -384,11 +390,11 @@ PerfLogImp::jobStart( } { - std::lock_guard lock(counter->second.mutex); + std::lock_guard const lock(counter->second.mutex); ++counter->second.value.started; counter->second.value.queuedDuration += dur; } - std::lock_guard lock(counters_.jobsMutex_); + std::lock_guard const lock(counters_.jobsMutex_); if (instance >= 0 && instance < counters_.jobs_.size()) counters_.jobs_[instance] = {type, startTime}; } @@ -406,11 +412,11 @@ PerfLogImp::jobFinish(JobType const type, microseconds dur, int instance) } { - std::lock_guard lock(counter->second.mutex); + std::lock_guard const lock(counter->second.mutex); ++counter->second.value.finished; counter->second.value.runningDuration += dur; } - std::lock_guard lock(counters_.jobsMutex_); + std::lock_guard const lock(counters_.jobsMutex_); if (instance >= 0 && instance < counters_.jobs_.size()) counters_.jobs_[instance] = {jtINVALID, steady_time_point()}; } @@ -418,7 +424,7 @@ PerfLogImp::jobFinish(JobType const type, microseconds dur, int instance) void PerfLogImp::resizeJobs(int const resize) { - std::lock_guard lock(counters_.jobsMutex_); + std::lock_guard const lock(counters_.jobsMutex_); if (resize > counters_.jobs_.size()) counters_.jobs_.resize(resize, {jtINVALID, steady_time_point()}); } @@ -429,7 +435,7 @@ PerfLogImp::rotate() if (setup_.perfLog.empty()) return; - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); rotate_ = true; cond_.notify_one(); } @@ -437,7 +443,7 @@ PerfLogImp::rotate() void PerfLogImp::start() { - if (setup_.perfLog.size()) + if (!setup_.perfLog.empty()) thread_ = std::thread(&PerfLogImp::run, this); } @@ -447,7 +453,7 @@ PerfLogImp::stop() if (thread_.joinable()) { { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); stop_ = true; cond_.notify_one(); } @@ -463,7 +469,7 @@ setup_PerfLog(Section const& section, boost::filesystem::path const& configDir) PerfLog::Setup setup; std::string perfLog; set(perfLog, "perf_log", section); - if (perfLog.size()) + if (!perfLog.empty()) { setup.perfLog = boost::filesystem::path(perfLog); if (setup.perfLog.is_relative()) @@ -472,7 +478,7 @@ setup_PerfLog(Section const& section, boost::filesystem::path const& configDir) } } - std::uint64_t logInterval; + std::uint64_t logInterval = 0; if (get_if_exists(section, "log_interval", logInterval)) setup.logInterval = std::chrono::seconds(logInterval); return setup; diff --git a/src/xrpld/rpc/BookChanges.h b/src/xrpld/rpc/BookChanges.h index b2b1ae793b..a318c80221 100644 --- a/src/xrpld/rpc/BookChanges.h +++ b/src/xrpld/rpc/BookChanges.h @@ -11,7 +11,7 @@ namespace Json { class Value; -} +} // namespace Json namespace xrpl { @@ -44,7 +44,7 @@ computeBookChanges(std::shared_ptr const& lpAccepted) continue; std::optional offerCancel; - uint16_t tt = tx.first->getFieldU16(sfTransactionType); + uint16_t const tt = tx.first->getFieldU16(sfTransactionType); switch (tt) { case ttOFFER_CANCEL: @@ -62,7 +62,7 @@ computeBookChanges(std::shared_ptr const& lpAccepted) for (auto const& node : tx.second->getFieldArray(sfAffectedNodes)) { SField const& metaType = node.getFName(); - uint16_t nodeType = node.getFieldU16(sfLedgerEntryType); + uint16_t const nodeType = node.getFieldU16(sfLedgerEntryType); // we only care about ltOFFER objects being modified or // deleted @@ -94,13 +94,13 @@ computeBookChanges(std::shared_ptr const& lpAccepted) // compute the difference in gets and pays actually // affected onto the offer - STAmount deltaGets = finalFields.getFieldAmount(sfTakerGets) - + STAmount const deltaGets = finalFields.getFieldAmount(sfTakerGets) - previousFields.getFieldAmount(sfTakerGets); - STAmount deltaPays = finalFields.getFieldAmount(sfTakerPays) - + STAmount const deltaPays = finalFields.getFieldAmount(sfTakerPays) - previousFields.getFieldAmount(sfTakerPays); - std::string g{to_string(deltaGets.issue())}; - std::string p{to_string(deltaPays.issue())}; + std::string const g{to_string(deltaGets.issue())}; + std::string const p{to_string(deltaPays.issue())}; bool const noswap = isXRP(deltaGets) ? true : (isXRP(deltaPays) ? false : (g < p)); @@ -111,7 +111,7 @@ computeBookChanges(std::shared_ptr const& lpAccepted) if (second == beast::zero) continue; - STAmount rate = divide(first, second, noIssue()); + STAmount const rate = divide(first, second, noIssue()); if (first < beast::zero) first = -first; @@ -125,9 +125,9 @@ computeBookChanges(std::shared_ptr const& lpAccepted) else ss << p << "|" << g; - std::optional domain = finalFields[~sfDomainID]; + std::optional const domain = finalFields[~sfDomainID]; - std::string key{ss.str()}; + std::string const key{ss.str()}; if (tally.find(key) == tally.end()) tally[key] = { @@ -174,8 +174,8 @@ computeBookChanges(std::shared_ptr const& lpAccepted) { Json::Value& inner = jvObj[jss::changes].append(Json::objectValue); - STAmount volA = std::get<0>(entry.second); - STAmount volB = std::get<1>(entry.second); + STAmount const volA = std::get<0>(entry.second); + STAmount const volB = std::get<1>(entry.second); inner[jss::currency_a] = (isXRP(volA) ? "XRP_drops" : to_string(volA.issue())); inner[jss::currency_b] = (isXRP(volB) ? "XRP_drops" : to_string(volB.issue())); diff --git a/src/xrpld/rpc/CTID.h b/src/xrpld/rpc/CTID.h index cbae1f4498..42efb4c157 100644 --- a/src/xrpld/rpc/CTID.h +++ b/src/xrpld/rpc/CTID.h @@ -39,7 +39,7 @@ encodeCTID(uint32_t ledgerSeq, uint32_t txnIndex, uint32_t networkID) noexcept if (ledgerSeq > maxLedgerSeq || txnIndex > maxTxnIndex || networkID > maxNetworkID) return std::nullopt; - uint64_t ctidValue = ((0xC000'0000ULL + static_cast(ledgerSeq)) << 32) | + uint64_t const ctidValue = ((0xC000'0000ULL + static_cast(ledgerSeq)) << 32) | ((static_cast(txnIndex) << 16) | networkID); std::stringstream buffer; @@ -101,9 +101,9 @@ decodeCTID(T const ctid) noexcept if ((ctidValue & ctidPrefixMask) != ctidPrefix) return std::nullopt; - uint32_t ledgerSeq = static_cast((ctidValue >> 32) & 0x0FFF'FFFF); - uint16_t txnIndex = static_cast((ctidValue >> 16) & 0xFFFF); - uint16_t networkID = static_cast(ctidValue & 0xFFFF); + uint32_t const ledgerSeq = static_cast((ctidValue >> 32) & 0x0FFF'FFFF); + uint16_t const txnIndex = static_cast((ctidValue >> 16) & 0xFFFF); + uint16_t const networkID = static_cast(ctidValue & 0xFFFF); return std::make_tuple(ledgerSeq, txnIndex, networkID); } diff --git a/src/xrpld/rpc/DeliveredAmount.h b/src/xrpld/rpc/DeliveredAmount.h index c882190ade..65f11e9736 100644 --- a/src/xrpld/rpc/DeliveredAmount.h +++ b/src/xrpld/rpc/DeliveredAmount.h @@ -8,7 +8,7 @@ namespace Json { class Value; -} +} // namespace Json namespace xrpl { diff --git a/src/xrpld/rpc/README.md b/src/xrpld/rpc/README.md index 749f3d0ed1..cf023770ea 100644 --- a/src/xrpld/rpc/README.md +++ b/src/xrpld/rpc/README.md @@ -6,7 +6,7 @@ By default, an RPC handler runs as an uninterrupted task on the JobQueue. This is fine for commands that are fast to compute but might not be acceptable for tasks that require multiple parts or are large, like a full ledger. -For this purpose, the rippled RPC handler allows _suspension with continuation_ +For this purpose, the xrpld RPC handler allows _suspension with continuation_ - a request to suspend execution of the RPC response and to continue it after some function or job has been executed. A default continuation is supplied diff --git a/src/xrpld/rpc/RPCCall.h b/src/xrpld/rpc/RPCCall.h index 54b73b65cf..9994195bb0 100644 --- a/src/xrpld/rpc/RPCCall.h +++ b/src/xrpld/rpc/RPCCall.h @@ -2,7 +2,7 @@ #include -#include +#include #include #include @@ -20,7 +20,7 @@ namespace xrpl { // // Improvements to be more strict and to provide better diagnostics are welcome. -/** Processes Ripple RPC calls. */ +/** Processes XRPL RPC calls. */ namespace RPCCall { int @@ -52,7 +52,7 @@ rpcCmdToJson( beast::Journal j); /** Internal invocation of RPC client. - * Used by both rippled command line as well as rippled unit tests + * Used by both xrpld command line as well as xrpld unit tests */ std::pair rpcClient( diff --git a/src/xrpld/rpc/RPCSub.h b/src/xrpld/rpc/RPCSub.h index e89f8f34e4..e96234ee02 100644 --- a/src/xrpld/rpc/RPCSub.h +++ b/src/xrpld/rpc/RPCSub.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -29,6 +30,6 @@ make_RPCSub( std::string const& strUrl, std::string const& strUsername, std::string const& strPassword, - Logs& logs); + ServiceRegistry& registry); } // namespace xrpl diff --git a/src/xrpld/rpc/ServerHandler.h b/src/xrpld/rpc/ServerHandler.h index af7ec4bf1c..2ffdc9556b 100644 --- a/src/xrpld/rpc/ServerHandler.h +++ b/src/xrpld/rpc/ServerHandler.h @@ -147,7 +147,7 @@ public: Handoff onHandoff( Session& session, - http_request_type&& request, + http_request_type&& request, // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) boost::asio::ip::tcp::endpoint const& remote_address) { return onHandoff(session, {}, std::forward(request), remote_address); @@ -182,7 +182,7 @@ private: Port const& port, std::string const& request, beast::IP::Endpoint const& remoteIPAddress, - Output&&, + Output const&, std::shared_ptr coro, std::string_view forwardedFor, std::string_view user); @@ -192,7 +192,7 @@ private: }; ServerHandler::Setup -setup_ServerHandler(Config const& c, std::ostream&& log); +setup_ServerHandler(Config const& c, std::ostream& log); std::unique_ptr make_ServerHandler( diff --git a/src/xrpld/rpc/Status.h b/src/xrpld/rpc/Status.h index c8ec9d0472..418ad9ccae 100644 --- a/src/xrpld/rpc/Status.h +++ b/src/xrpld/rpc/Status.h @@ -29,7 +29,7 @@ public: // The enable_if allows only integers (not enums). Prevents enum narrowing. template ::value>> - Status(T code, Strings d = {}) : type_(Type::none), code_(code), messages_(std::move(d)) + Status(T code, Strings d = {}) : code_(code), messages_(std::move(d)) { } diff --git a/src/xrpld/app/paths/AccountCurrencies.cpp b/src/xrpld/rpc/detail/AccountCurrencies.cpp similarity index 97% rename from src/xrpld/app/paths/AccountCurrencies.cpp rename to src/xrpld/rpc/detail/AccountCurrencies.cpp index 92ba61e00e..c839b1475c 100644 --- a/src/xrpld/app/paths/AccountCurrencies.cpp +++ b/src/xrpld/rpc/detail/AccountCurrencies.cpp @@ -1,4 +1,4 @@ -#include +#include namespace xrpl { diff --git a/src/xrpld/app/paths/AccountCurrencies.h b/src/xrpld/rpc/detail/AccountCurrencies.h similarity index 89% rename from src/xrpld/app/paths/AccountCurrencies.h rename to src/xrpld/rpc/detail/AccountCurrencies.h index d8459de7f2..76c531cb9b 100644 --- a/src/xrpld/app/paths/AccountCurrencies.h +++ b/src/xrpld/rpc/detail/AccountCurrencies.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include diff --git a/src/xrpld/rpc/detail/DeliveredAmount.cpp b/src/xrpld/rpc/detail/DeliveredAmount.cpp index 3e89edc1f9..e2f5bd8cd9 100644 --- a/src/xrpld/rpc/detail/DeliveredAmount.cpp +++ b/src/xrpld/rpc/detail/DeliveredAmount.cpp @@ -71,13 +71,8 @@ canHaveDeliveredAmount( TxType const tt{serializedTx->getTxnType()}; // Transaction type should be ttPAYMENT, ttACCOUNT_DELETE or ttCHECK_CASH // and if the transaction failed nothing could have been delivered. - if ((tt == ttPAYMENT || tt == ttCHECK_CASH || tt == ttACCOUNT_DELETE) && - transactionMeta.getResultTER() == tesSUCCESS) - { - return true; - } - - return false; + return (tt == ttPAYMENT || tt == ttCHECK_CASH || tt == ttACCOUNT_DELETE) && + transactionMeta.getResultTER() == tesSUCCESS; } void diff --git a/src/xrpld/rpc/detail/Handler.cpp b/src/xrpld/rpc/detail/Handler.cpp index ec970b107e..05fc1cb0b4 100644 --- a/src/xrpld/rpc/detail/Handler.cpp +++ b/src/xrpld/rpc/detail/Handler.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include @@ -42,9 +42,13 @@ handle(JsonContext& context, Object& object) auto status = handler.check(); if (status) + { status.inject(object); + } else + { handler.writeResult(object); + } return status; } @@ -71,7 +75,7 @@ Handler const handlerArray[]{ {"account_nfts", byRef(&doAccountNFTs), Role::USER, NO_CONDITION}, {"account_objects", byRef(&doAccountObjects), Role::USER, NO_CONDITION}, {"account_offers", byRef(&doAccountOffers), Role::USER, NO_CONDITION}, - {"account_tx", byRef(&doAccountTxJson), Role::USER, NO_CONDITION}, + {"account_tx", byRef(&doAccountTx), Role::USER, NO_CONDITION}, {"amm_info", byRef(&doAMMInfo), Role::USER, NO_CONDITION}, {"blacklist", byRef(&doBlackList), Role::ADMIN, NO_CONDITION}, {"book_changes", byRef(&doBookChanges), Role::USER, NO_CONDITION}, @@ -146,7 +150,7 @@ private: // Use with equal_range to enforce that API range of a newly added handler // does not overlap with API range of an existing handler with same name - [[nodiscard]] bool + [[nodiscard]] static bool overlappingApiVersion( std::pair range, unsigned minVer, @@ -172,9 +176,11 @@ private: { if (overlappingApiVersion( table_.equal_range(entry.name_), entry.minApiVer_, entry.maxApiVer_)) + { LogicError( std::string("Handler for ") + entry.name_ + " overlaps with an existing handler"); + } table_.insert({entry.name_, entry}); } @@ -232,9 +238,11 @@ private: table_.equal_range(HandlerImpl::name), HandlerImpl::minApiVer, HandlerImpl::maxApiVer)) + { LogicError( std::string("Handler for ") + HandlerImpl::name + " overlaps with an existing handler"); + } table_.insert({HandlerImpl::name, handlerFrom()}); } diff --git a/src/xrpld/rpc/detail/Handler.h b/src/xrpld/rpc/detail/Handler.h index 908cb67750..3628962a69 100644 --- a/src/xrpld/rpc/detail/Handler.h +++ b/src/xrpld/rpc/detail/Handler.h @@ -10,7 +10,7 @@ namespace Json { class Object; -} +} // namespace Json namespace xrpl { namespace RPC { diff --git a/src/xrpld/rpc/detail/LegacyPathFind.cpp b/src/xrpld/rpc/detail/LegacyPathFind.cpp index b0fa07d676..5b5bcc540b 100644 --- a/src/xrpld/rpc/detail/LegacyPathFind.cpp +++ b/src/xrpld/rpc/detail/LegacyPathFind.cpp @@ -9,7 +9,7 @@ namespace xrpl { namespace RPC { -LegacyPathFind::LegacyPathFind(bool isAdmin, Application& app) : m_isOk(false) +LegacyPathFind::LegacyPathFind(bool isAdmin, Application& app) { if (isAdmin) { diff --git a/src/xrpld/rpc/detail/LegacyPathFind.h b/src/xrpld/rpc/detail/LegacyPathFind.h index 139075b53d..3d45bc9cfd 100644 --- a/src/xrpld/rpc/detail/LegacyPathFind.h +++ b/src/xrpld/rpc/detail/LegacyPathFind.h @@ -23,7 +23,7 @@ public: private: static std::atomic inProgress; - bool m_isOk; + bool m_isOk{false}; }; } // namespace RPC diff --git a/src/xrpld/rpc/detail/MPTokenIssuanceID.cpp b/src/xrpld/rpc/detail/MPTokenIssuanceID.cpp index f818c72b59..ef8bc448ee 100644 --- a/src/xrpld/rpc/detail/MPTokenIssuanceID.cpp +++ b/src/xrpld/rpc/detail/MPTokenIssuanceID.cpp @@ -17,7 +17,7 @@ canHaveMPTokenIssuanceID( return false; // if the transaction failed nothing could have been delivered. - if (transactionMeta.getResultTER() != tesSUCCESS) + if (!isTesSuccess(transactionMeta.getResultTER())) return false; return true; diff --git a/src/xrpld/app/paths/PathRequest.cpp b/src/xrpld/rpc/detail/PathRequest.cpp similarity index 95% rename from src/xrpld/app/paths/PathRequest.cpp rename to src/xrpld/rpc/detail/PathRequest.cpp index c6d0f59ff0..fe85c519eb 100644 --- a/src/xrpld/app/paths/PathRequest.cpp +++ b/src/xrpld/rpc/detail/PathRequest.cpp @@ -1,12 +1,11 @@ #include -#include -#include -#include -#include #include +#include +#include +#include +#include #include -#include #include #include #include @@ -24,7 +23,7 @@ PathRequest::PathRequest( Application& app, std::shared_ptr const& subscriber, int id, - PathRequests& owner, + PathRequestManager& owner, beast::Journal journal) : app_(app) , m_journal(journal) @@ -47,7 +46,7 @@ PathRequest::PathRequest( std::function const& completion, Resource::Consumer& consumer, int id, - PathRequests& owner, + PathRequestManager& owner, beast::Journal journal) : app_(app) , m_journal(journal) @@ -93,7 +92,7 @@ PathRequest::~PathRequest() bool PathRequest::isNew() { - std::lock_guard sl(mIndexLock); + std::lock_guard const sl(mIndexLock); // does this path request still need its first full path return mLastIndex == 0; @@ -102,7 +101,7 @@ PathRequest::isNew() bool PathRequest::needsUpdate(bool newOnly, LedgerIndex index) { - std::lock_guard sl(mIndexLock); + std::lock_guard const sl(mIndexLock); if (mInProgress) { @@ -134,7 +133,7 @@ PathRequest::hasCompletion() void PathRequest::updateComplete() { - std::lock_guard sl(mIndexLock); + std::lock_guard const sl(mIndexLock); XRPL_ASSERT(mInProgress, "xrpl::PathRequest::updateComplete : in progress"); mInProgress = false; @@ -191,7 +190,7 @@ PathRequest::isValid(std::shared_ptr const& crCache) } else { - bool const disallowXRP(sleDest->getFlags() & lsfDisallowXRP); + bool const disallowXRP((sleDest->getFlags() & lsfDisallowXRP) != 0u); auto usDestCurrID = accountDestCurrencies(*raDstAccount, crCache, !disallowXRP); @@ -406,10 +405,8 @@ PathRequest::parseJson(Json::Value const& jvParams) jvStatus = rpcError(rpcDOMAIN_MALFORMED); return PFR_PJ_INVALID; } - else - { - domain = num; - } + + domain = num; } return PFR_PJ_NOCHANGE; @@ -419,7 +416,7 @@ Json::Value PathRequest::doClose() { JLOG(m_journal.debug()) << iIdentifier << " closed"; - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); jvStatus[jss::closed] = true; return jvStatus; } @@ -427,7 +424,7 @@ PathRequest::doClose() Json::Value PathRequest::doStatus(Json::Value const&) { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); jvStatus[jss::status] = jss::success; return jvStatus; } @@ -461,9 +458,13 @@ PathRequest::getPathFinder( domain, app_); if (pathfinder->findPaths(level, continueCallback)) + { pathfinder->computePathRanks(max_paths_, continueCallback); + } else + { pathfinder.reset(); // It's a bad request - clear it. + } return currency_map[currency] = std::move(pathfinder); } @@ -526,7 +527,7 @@ PathRequest::findPaths( return *raSrcAccount; }(); - STAmount saMaxAmount = + STAmount const saMaxAmount = saSendMax.value_or(STAmount(Issue{issue.currency, sourceAccount}, 1u, 0, true)); JLOG(m_journal.debug()) << iIdentifier << " Paths found, calling rippleCalc"; @@ -544,7 +545,7 @@ PathRequest::findPaths( *raSrcAccount, // --> Account sending from. ps, // --> Path set. domain, // --> Domain. - app_.logs(), + app_, &rcInput); if (!convert_all_ && !fullLiquidityPath.empty() && @@ -563,9 +564,9 @@ PathRequest::findPaths( *raSrcAccount, // --> Account sending from. ps, // --> Path set. domain, // --> Domain. - app_.logs()); + app_); - if (rc.result() != tesSUCCESS) + if (!isTesSuccess(rc.result())) { JLOG(m_journal.warn()) << iIdentifier << " Failed with covering path " << transHuman(rc.result()); @@ -607,7 +608,7 @@ PathRequest::findPaths( after four source currencies, 50 - (4 * 4) = 34. */ int const size = sourceCurrencies.size(); - consumer_.charge({std::clamp(size * size + 34, 50, 400), "path update"}); + consumer_.charge({std::clamp((size * size) + 34, 50, 400), "path update"}); return true; } @@ -621,7 +622,7 @@ PathRequest::doUpdate( JLOG(m_journal.debug()) << iIdentifier << " update " << (fast ? "fast" : "normal"); { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); if (!isValid(cache)) return jvStatus; @@ -646,15 +647,19 @@ PathRequest::doUpdate( if (jvId) newStatus[jss::id] = jvId; - bool loaded = app_.getFeeTrack().isLoadedLocal(); + bool const loaded = app_.getFeeTrack().isLoadedLocal(); if (iLevel == 0) { // first pass if (loaded || fast) + { iLevel = app_.config().PATH_SEARCH_FAST; + } else + { iLevel = app_.config().PATH_SEARCH; + } } else if ((iLevel == app_.config().PATH_SEARCH_FAST) && !fast) { @@ -705,7 +710,7 @@ PathRequest::doUpdate( } { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); jvStatus = newStatus; } diff --git a/src/xrpld/app/paths/PathRequest.h b/src/xrpld/rpc/detail/PathRequest.h similarity index 93% rename from src/xrpld/app/paths/PathRequest.h rename to src/xrpld/rpc/detail/PathRequest.h index fde499a312..db173e307b 100644 --- a/src/xrpld/app/paths/PathRequest.h +++ b/src/xrpld/rpc/detail/PathRequest.h @@ -1,11 +1,11 @@ #pragma once -#include -#include -#include +#include +#include #include #include +#include #include #include @@ -20,7 +20,7 @@ namespace xrpl { // The request issuer must maintain a strong pointer class RippleLineCache; -class PathRequests; +class PathRequestManager; // Return values from parseJson <0 = invalid, >0 = valid #define PFR_PJ_INVALID -1 @@ -43,7 +43,7 @@ public: Application& app, std::shared_ptr const& subscriber, int id, - PathRequests&, + PathRequestManager&, beast::Journal journal); // ripple_path_find semantics @@ -53,7 +53,7 @@ public: std::function const& completion, Resource::Consumer& consumer, int id, - PathRequests&, + PathRequestManager&, beast::Journal journal); ~PathRequest(); @@ -119,7 +119,7 @@ private: std::recursive_mutex mLock; - PathRequests& mOwner; + PathRequestManager& mOwner; std::weak_ptr wpSubscriber; // Who this request came from std::function fCompletion; @@ -139,7 +139,7 @@ private: std::optional domain; - bool convert_all_; + bool convert_all_{}; std::recursive_mutex mIndexLock; LedgerIndex mLastIndex; diff --git a/src/xrpld/app/paths/PathRequests.cpp b/src/xrpld/rpc/detail/PathRequestManager.cpp similarity index 90% rename from src/xrpld/app/paths/PathRequests.cpp rename to src/xrpld/rpc/detail/PathRequestManager.cpp index 35a6c7aa48..4455e304e5 100644 --- a/src/xrpld/app/paths/PathRequests.cpp +++ b/src/xrpld/rpc/detail/PathRequestManager.cpp @@ -1,8 +1,7 @@ #include #include -#include +#include -#include #include #include #include @@ -16,9 +15,9 @@ namespace xrpl { Get the correct ledger to use. */ std::shared_ptr -PathRequests::getLineCache(std::shared_ptr const& ledger, bool authoritative) +PathRequestManager::getLineCache(std::shared_ptr const& ledger, bool authoritative) { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); auto lineCache = lineCache_.lock(); @@ -37,13 +36,13 @@ PathRequests::getLineCache(std::shared_ptr const& ledger, bool a // weak_ptr, and will immediately discard it if there are no other // references. lineCache_ = lineCache = - std::make_shared(ledger, app_.journal("RippleLineCache")); + std::make_shared(ledger, app_.getJournal("RippleLineCache")); } return lineCache; } void -PathRequests::updateAll(std::shared_ptr const& inLedger) +PathRequestManager::updateAll(std::shared_ptr const& inLedger) { auto event = app_.getJobQueue().makeLoadEvent(jtPATH_FIND, "PathRequest::updateAll"); @@ -52,7 +51,7 @@ PathRequests::updateAll(std::shared_ptr const& inLedger) // Get the ledger and cache we should be using { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); requests = requests_; cache = getLineCache(inLedger, true); } @@ -95,7 +94,9 @@ PathRequests::updateAll(std::shared_ptr const& inLedger) return (bool)getSubscriber(request); }; if (!request->needsUpdate(newRequests, cache->getLedger()->seq())) + { remove = false; + } else { if (auto ipSub = getSubscriber(request)) @@ -129,7 +130,7 @@ PathRequests::updateAll(std::shared_ptr const& inLedger) if (remove) { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); // Remove any dangling weak pointers or weak // pointers that refer to this path request. @@ -174,7 +175,7 @@ PathRequests::updateAll(std::shared_ptr const& inLedger) std::shared_ptr lastCache; { // Get the latest requests, cache, and ledger for next pass - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); if (requests_.empty()) break; @@ -189,16 +190,16 @@ PathRequests::updateAll(std::shared_ptr const& inLedger) } bool -PathRequests::requestsPending() const +PathRequestManager::requestsPending() const { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); return !requests_.empty(); } void -PathRequests::insertPathRequest(PathRequest::pointer const& req) +PathRequestManager::insertPathRequest(PathRequest::pointer const& req) { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); // Insert after any older unserviced requests but before // any serviced requests @@ -214,7 +215,7 @@ PathRequests::insertPathRequest(PathRequest::pointer const& req) // Make a new-style path_find request Json::Value -PathRequests::makePathRequest( +PathRequestManager::makePathRequest( std::shared_ptr const& subscriber, std::shared_ptr const& inLedger, Json::Value const& requestJson) @@ -234,7 +235,7 @@ PathRequests::makePathRequest( // Make an old-style ripple_path_find request Json::Value -PathRequests::makeLegacyPathRequest( +PathRequestManager::makeLegacyPathRequest( PathRequest::pointer& req, std::function completion, Resource::Consumer& consumer, @@ -267,12 +268,12 @@ PathRequests::makeLegacyPathRequest( } Json::Value -PathRequests::doLegacyPathRequest( +PathRequestManager::doLegacyPathRequest( Resource::Consumer& consumer, std::shared_ptr const& inLedger, Json::Value const& request) { - auto cache = std::make_shared(inLedger, app_.journal("RippleLineCache")); + auto cache = std::make_shared(inLedger, app_.getJournal("RippleLineCache")); auto req = std::make_shared(app_, [] {}, consumer, ++mLastIdentifier, *this, mJournal); diff --git a/src/xrpld/app/paths/PathRequests.h b/src/xrpld/rpc/detail/PathRequestManager.h similarity index 94% rename from src/xrpld/app/paths/PathRequests.h rename to src/xrpld/rpc/detail/PathRequestManager.h index 98f4be9fd7..0f884de5f3 100644 --- a/src/xrpld/app/paths/PathRequests.h +++ b/src/xrpld/rpc/detail/PathRequestManager.h @@ -1,8 +1,8 @@ #pragma once #include -#include -#include +#include +#include #include #include @@ -10,11 +10,11 @@ namespace xrpl { -class PathRequests +class PathRequestManager { public: /** A collection of all PathRequest instances. */ - PathRequests( + PathRequestManager( Application& app, beast::Journal journal, beast::insight::Collector::ptr const& collector) diff --git a/src/xrpld/app/paths/Pathfinder.cpp b/src/xrpld/rpc/detail/Pathfinder.cpp similarity index 95% rename from src/xrpld/app/paths/Pathfinder.cpp rename to src/xrpld/rpc/detail/Pathfinder.cpp index 254db35ea5..4749caaccb 100644 --- a/src/xrpld/app/paths/Pathfinder.cpp +++ b/src/xrpld/rpc/detail/Pathfinder.cpp @@ -1,9 +1,8 @@ #include -#include -#include -#include +#include +#include +#include -#include #include #include #include @@ -95,7 +94,7 @@ struct PathCost }; using PathCostList = std::vector; -static PathTable mPathTable; +PathTable mPathTable; std::string pathTypeToString(Pathfinder::PathType const& type) @@ -167,7 +166,7 @@ Pathfinder::Pathfinder( , mLedger(cache->getLedger()) , mRLCache(cache) , app_(app) - , j_(app.journal("Pathfinder")) + , j_(app.getJournal("Pathfinder")) { XRPL_ASSERT( !uSrcIssuer || isXRP(uSrcCurrency) == isXRP(uSrcIssuer.value()), @@ -207,7 +206,7 @@ Pathfinder::findPaths(int searchLevel, std::function const& continue m_loadEvent = app_.getJobQueue().makeLoadEvent(jtPATH_FIND, "FindPath"); auto currencyIsXRP = isXRP(mSrcCurrency); - bool useIssuerAccount = mSrcIssuer && !currencyIsXRP && !isXRP(*mSrcIssuer); + bool const useIssuerAccount = mSrcIssuer && !currencyIsXRP && !isXRP(*mSrcIssuer); auto& account = useIssuerAccount ? *mSrcIssuer : mSrcAccount; auto issuer = currencyIsXRP ? AccountID() : account; mSource = STPathElement(account, mSrcCurrency, issuer); @@ -223,8 +222,8 @@ Pathfinder::findPaths(int searchLevel, std::function const& continue return false; } - bool bSrcXrp = isXRP(mSrcCurrency); - bool bDstXrp = isXRP(mDstAmount.getCurrency()); + bool const bSrcXrp = isXRP(mSrcCurrency); + bool const bDstXrp = isXRP(mDstAmount.getCurrency()); if (!mLedger->exists(keylet::account(mSrcAccount))) { @@ -260,7 +259,7 @@ Pathfinder::findPaths(int searchLevel, std::function const& continue // Now compute the payment type from the types of the source and destination // currencies. - PaymentType paymentType; + PaymentType paymentType = pt_XRP_to_XRP; if (bSrcXrp && bDstXrp) { // XRP -> XRP @@ -345,10 +344,10 @@ Pathfinder::getPathLiquidity( mSrcAccount, pathSet, mDomain, - app_.logs(), + app_, &rcInput); // If we can't get even the minimum liquidity requested, we're done. - if (rc.result() != tesSUCCESS) + if (!isTesSuccess(rc.result())) return rc.result(); qualityOut = getRate(rc.actualAmountOut, rc.actualAmountIn); @@ -366,7 +365,7 @@ Pathfinder::getPathLiquidity( mSrcAccount, pathSet, mDomain, - app_.logs(), + app_, &rcInput); // If we found further liquidity, add it into the result. @@ -404,7 +403,7 @@ Pathfinder::computePathRanks(int maxPaths, std::function const& cont mSrcAccount, STPathSet(), mDomain, - app_.logs(), + app_, &rcInput); if (rc.result() == tesSUCCESS) @@ -491,10 +490,10 @@ Pathfinder::rankPaths( if (!currentPath.empty()) { STAmount liquidity; - uint64_t uQuality; + uint64_t uQuality = 0; auto const resultCode = getPathLiquidity(currentPath, saMinDstAmount, liquidity, uQuality); - if (resultCode != tesSUCCESS) + if (!isTesSuccess(resultCode)) { JLOG(j_.debug()) << "findPaths: dropping : " << transToken(resultCode) << ": " << currentPath.getJson(JsonOptions::none); @@ -574,17 +573,29 @@ Pathfinder::getBestPaths( bool useExtraPath = false; if (pathsIterator == mPathRanks.end()) + { useExtraPath = true; + } else if (extraPathsIterator == extraPathRanks.end()) + { usePath = true; + } else if (extraPathsIterator->quality < pathsIterator->quality) + { useExtraPath = true; + } else if (extraPathsIterator->quality > pathsIterator->quality) + { usePath = true; + } else if (extraPathsIterator->liquidity > pathsIterator->liquidity) + { useExtraPath = true; + } else if (extraPathsIterator->liquidity < pathsIterator->liquidity) + { usePath = true; + } else { // Risk is high they have identical liquidity @@ -603,7 +614,7 @@ Pathfinder::getBestPaths( ++pathsIterator; auto iPathsLeft = maxPaths - bestPaths.size(); - if (!(iPathsLeft > 0 || fullLiquidityPath.empty())) + if (iPathsLeft <= 0 && !fullLiquidityPath.empty()) break; if (path.empty()) @@ -663,9 +674,9 @@ Pathfinder::getBestPaths( bool Pathfinder::issueMatchesOrigin(Issue const& issue) { - bool matchingCurrency = (issue.currency == mSrcCurrency); - bool matchingAccount = isXRP(issue.currency) || (mSrcIssuer && issue.account == mSrcIssuer) || - issue.account == mSrcAccount; + bool const matchingCurrency = (issue.currency == mSrcCurrency); + bool const matchingAccount = isXRP(issue.currency) || + (mSrcIssuer && issue.account == mSrcIssuer) || issue.account == mSrcAccount; return matchingCurrency && matchingAccount; } @@ -692,7 +703,7 @@ Pathfinder::getPathsOut( if (!sleAccount) return 0; - int aFlags = sleAccount->getFieldU32(sfFlags); + int const aFlags = sleAccount->getFieldU32(sfFlags); bool const bAuthRequired = (aFlags & lsfRequireAuth) != 0; bool const bFrozen = ((aFlags & lsfGlobalFreeze) != 0); @@ -783,7 +794,7 @@ Pathfinder::addPathsForType( JLOG(j_.debug()) << "getPaths< adding onto '" << pathTypeToString(parentPathType) << "' to get '" << pathTypeToString(pathType) << "'"; - int initialSize = mCompletePaths.size(); + int const initialSize = mCompletePaths.size(); // Add the last NodeType to the lists. auto nodeType = pathType.back(); @@ -838,7 +849,7 @@ Pathfinder::isNoRipple( auto const flag((toAccount > fromAccount) ? lsfHighNoRipple : lsfLowNoRipple); - return sleRipple && (sleRipple->getFieldU32(sfFlags) & flag); + return sleRipple && ((sleRipple->getFieldU32(sfFlags) & flag) != 0u); } // Does this path end on an account-to-account link whose last account has @@ -852,7 +863,7 @@ Pathfinder::isNoRippleOut(STPath const& currentPath) // Last link must be an account. STPathElement const& endElement = currentPath.back(); - if (!(endElement.getNodeType() & STPathElement::typeAccount)) + if ((endElement.getNodeType() & STPathElement::typeAccount) == 0u) return false; // If there's only one item in the path, return true if that item specifies @@ -899,7 +910,7 @@ Pathfinder::addLink( << " completePaths size=" << mCompletePaths.size(); JLOG(j_.trace()) << currentPath.getJson(JsonOptions::none); - if (addFlags & afADD_ACCOUNTS) + if ((addFlags & afADD_ACCOUNTS) != 0u) { // add accounts if (bOnXRP) @@ -918,10 +929,10 @@ Pathfinder::addLink( if (sleEnd) { - bool const bRequireAuth(sleEnd->getFieldU32(sfFlags) & lsfRequireAuth); + bool const bRequireAuth((sleEnd->getFieldU32(sfFlags) & lsfRequireAuth) != 0u); bool const bIsEndCurrency(uEndCurrency == mDstAmount.getCurrency()); bool const bIsNoRippleOut(isNoRippleOut(currentPath)); - bool const bDestOnly(addFlags & afAC_LAST); + bool const bDestOnly((addFlags & afAC_LAST) != 0u); if (auto const lines = mRLCache->getRippleLines( uEndAccount, @@ -945,7 +956,7 @@ Pathfinder::addLink( continue; } - bool bToDestination = acct == mEffectiveDst; + bool const bToDestination = acct == mEffectiveDst; if (bDestOnly && !bToDestination) { @@ -993,14 +1004,14 @@ Pathfinder::addLink( else { // save this candidate - int out = getPathsOut( + int const out = getPathsOut( uEndCurrency, acct, direction, bIsEndCurrency, mEffectiveDst, continueCallback); - if (out) + if (out != 0) candidates.push_back({out, acct}); } } @@ -1020,9 +1031,13 @@ Pathfinder::addLink( int count = candidates.size(); // allow more paths from source if ((count > 10) && (uEndAccount != mSrcAccount)) + { count = 10; + } else if (count > 50) + { count = 50; + } auto it = candidates.begin(); while (count-- != 0) @@ -1030,7 +1045,7 @@ Pathfinder::addLink( if (continueCallback && !continueCallback()) return; // Add accounts to incompletePaths - STPathElement pathElement( + STPathElement const pathElement( STPathElement::typeAccount, it->account, uEndCurrency, it->account); incompletePaths.assembleAdd(currentPath, pathElement); ++it; @@ -1044,22 +1059,22 @@ Pathfinder::addLink( } } } - if (addFlags & afADD_BOOKS) + if ((addFlags & afADD_BOOKS) != 0u) { // add order books - if (addFlags & afOB_XRP) + if ((addFlags & afOB_XRP) != 0u) { // to XRP only if (!bOnXRP && app_.getOrderBookDB().isBookToXRP({uEndCurrency, uEndIssuer}, mDomain)) { - STPathElement pathElement( + STPathElement const pathElement( STPathElement::typeCurrency, xrpAccount(), xrpCurrency(), xrpAccount()); incompletePaths.assembleAdd(currentPath, pathElement); } } else { - bool bDestOnly = (addFlags & afOB_LAST) != 0; + bool const bDestOnly = (addFlags & afOB_LAST) != 0; auto books = app_.getOrderBookDB().getBooksByTakerPays({uEndCurrency, uEndIssuer}, mDomain); JLOG(j_.trace()) << books.size() << " books found from this currency/issuer"; @@ -1090,7 +1105,9 @@ Pathfinder::addLink( addUniquePath(mCompletePaths, newPath); } else + { incompletePaths.push_back(newPath); + } } else if (!currentPath.hasSeen( book.out.account, book.out.currency, book.out.account)) @@ -1158,6 +1175,7 @@ makePath(char const* string) while (true) { + // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (*string++) { case 's': // source diff --git a/src/xrpld/app/paths/Pathfinder.h b/src/xrpld/rpc/detail/Pathfinder.h similarity index 97% rename from src/xrpld/app/paths/Pathfinder.h rename to src/xrpld/rpc/detail/Pathfinder.h index 03f2d6f038..662d59ac9a 100644 --- a/src/xrpld/app/paths/Pathfinder.h +++ b/src/xrpld/rpc/detail/Pathfinder.h @@ -1,10 +1,10 @@ #pragma once -#include -#include +#include #include #include +#include #include #include @@ -82,10 +82,10 @@ public: struct PathRank { - std::uint64_t quality; - std::uint64_t length; + std::uint64_t quality{}; + std::uint64_t length{}; STAmount liquidity; - int index; + int index{}; }; private: diff --git a/src/xrpld/app/paths/detail/PathfinderUtils.h b/src/xrpld/rpc/detail/PathfinderUtils.h similarity index 100% rename from src/xrpld/app/paths/detail/PathfinderUtils.h rename to src/xrpld/rpc/detail/PathfinderUtils.h diff --git a/src/xrpld/rpc/detail/RPCCall.cpp b/src/xrpld/rpc/detail/RPCCall.cpp index 134cbb34f8..4396037f2e 100644 --- a/src/xrpld/rpc/detail/RPCCall.cpp +++ b/src/xrpld/rpc/detail/RPCCall.cpp @@ -104,31 +104,29 @@ private: // optionally followed by a forward slash and some other characters // (the issuer). // https://www.boost.org/doc/libs/1_82_0/libs/regex/doc/html/boost_regex/syntax/perl_syntax.html - static boost::regex reCurIss("\\`([][:alnum:]<>(){}[|?!@#$%^&*]{3})(?:/(.+))?\\'"); + static boost::regex const reCurIss("\\`([][:alnum:]<>(){}[|?!@#$%^&*]{3})(?:/(.+))?\\'"); boost::smatch smMatch; if (boost::regex_match(strCurrencyIssuer, smMatch, reCurIss)) { Json::Value jvResult(Json::objectValue); - std::string strCurrency = smMatch[1]; - std::string strIssuer = smMatch[2]; + std::string const strCurrency = smMatch[1]; + std::string const strIssuer = smMatch[2]; jvResult[jss::currency] = strCurrency; - if (strIssuer.length()) + if (!strIssuer.empty()) { - // Could confirm issuer is a valid Ripple address. + // Could confirm issuer is a valid XRPL address. jvResult[jss::issuer] = strIssuer; } return jvResult; } - else - { - return RPC::make_param_error( - std::string("Invalid currency/issuer '") + strCurrencyIssuer + "'"); - } + + return RPC::make_param_error( + std::string("Invalid currency/issuer '") + strCurrencyIssuer + "'"); } static bool @@ -151,6 +149,7 @@ private: using parseFuncPtr = Json::Value (RPCParser::*)(Json::Value const& jvParams); Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseAsIs(Json::Value const& jvParams) { Json::Value v(Json::objectValue); @@ -162,6 +161,7 @@ private: } Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseInternal(Json::Value const& jvParams) { Json::Value v(Json::objectValue); @@ -178,6 +178,7 @@ private: } Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseManifest(Json::Value const& jvParams) { if (jvParams.size() == 1) @@ -198,10 +199,11 @@ private: // fetch_info [clear] Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseFetchInfo(Json::Value const& jvParams) { Json::Value jvRequest(Json::objectValue); - unsigned int iParams = jvParams.size(); + unsigned int const iParams = jvParams.size(); if (iParams != 0) jvRequest[jvParams[0u].asString()] = true; @@ -212,6 +214,7 @@ private: // account_tx accountID [ledger_min [ledger_max [limit [offset]]]] [binary] // [count] [descending] Json::Value + // NOLINTNEXTLINE(readability-make-member-function-const) parseAccountTransactions(Json::Value const& jvParams) { Json::Value jvRequest(Json::objectValue); @@ -259,8 +262,8 @@ private: } else { - std::int64_t uLedgerMin = jvParams[1u].asInt(); - std::int64_t uLedgerMax = jvParams[2u].asInt(); + std::int64_t const uLedgerMin = jvParams[1u].asInt(); + std::int64_t const uLedgerMax = jvParams[2u].asInt(); if (uLedgerMax != -1 && uLedgerMax < uLedgerMin) { @@ -287,6 +290,7 @@ private: // // Mnemonic: taker pays --> offer --> taker gets Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseBookOffers(Json::Value const& jvParams) { Json::Value jvRequest(Json::objectValue); @@ -298,19 +302,15 @@ private: { return jvTakerPays; } - else - { - jvRequest[jss::taker_pays] = jvTakerPays; - } + + jvRequest[jss::taker_pays] = jvTakerPays; if (isRpcError(jvTakerGets)) { return jvTakerGets; } - else - { - jvRequest[jss::taker_gets] = jvTakerGets; - } + + jvRequest[jss::taker_gets] = jvTakerGets; if (jvParams.size() >= 3) { @@ -324,7 +324,7 @@ private: { try { - int iLimit = jvParams[4u].asInt(); + int const iLimit = jvParams[4u].asInt(); if (iLimit > 0) jvRequest[jss::limit] = iLimit; @@ -339,8 +339,8 @@ private: { try { - int bProof = jvParams[5u].asInt(); - if (bProof) + int const bProof = jvParams[5u].asInt(); + if (bProof != 0) jvRequest[jss::proof] = true; } catch (std::exception const&) @@ -357,24 +357,30 @@ private: // can_delete [||now|always|never] Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseCanDelete(Json::Value const& jvParams) { Json::Value jvRequest(Json::objectValue); - if (!jvParams.size()) + if (jvParams.size() == 0u) return jvRequest; - std::string input = jvParams[0u].asString(); + std::string const input = jvParams[0u].asString(); if (input.find_first_not_of("0123456789") == std::string::npos) + { jvRequest["can_delete"] = jvParams[0u].asUInt(); + } else + { jvRequest["can_delete"] = input; + } return jvRequest; } // connect [port] Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseConnect(Json::Value const& jvParams) { Json::Value jvRequest(Json::objectValue); @@ -389,7 +395,7 @@ private: // handle case where there is one argument of the form ip:port if (std::count(ip.begin(), ip.end(), ':') == 1) { - std::size_t colon = ip.find_last_of(":"); + std::size_t const colon = ip.find_last_of(':'); jvRequest[jss::ip] = std::string{ip, 0, colon}; jvRequest[jss::port] = Json::Value{std::string{ip, colon + 1}}.asUInt(); return jvRequest; @@ -403,6 +409,7 @@ private: // deposit_authorized // [ [, ...]] Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseDepositAuthorized(Json::Value const& jvParams) { Json::Value jvRequest(Json::objectValue); @@ -425,6 +432,7 @@ private: // Return an error for attempting to subscribe/unsubscribe via RPC. Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseEvented(Json::Value const& jvParams) { return rpcError(rpcNO_EVENTS); @@ -432,6 +440,7 @@ private: // feature [] [accept|reject] Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseFeature(Json::Value const& jvParams) { Json::Value jvRequest(Json::objectValue); @@ -447,11 +456,17 @@ private: // determines whether an amendment is vetoed - so "reject" means // that jss::vetoed is true. if (boost::iequals(action, "reject")) + { jvRequest[jss::vetoed] = Json::Value(true); + } else if (boost::iequals(action, "accept")) + { jvRequest[jss::vetoed] = Json::Value(false); + } else + { return rpcError(rpcINVALID_PARAMS); + } } return jvRequest; @@ -459,11 +474,12 @@ private: // get_counts [] Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseGetCounts(Json::Value const& jvParams) { Json::Value jvRequest(Json::objectValue); - if (jvParams.size()) + if (jvParams.size() != 0u) jvRequest[jss::min_count] = jvParams[0u].asUInt(); return jvRequest; @@ -472,6 +488,7 @@ private: // sign_for offline // sign_for Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseSignFor(Json::Value const& jvParams) { bool const bOffline = 4 == jvParams.size() && jvParams[3u].asString() == "offline"; @@ -541,11 +558,9 @@ private: jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0" && jv.isMember(jss::id) && jv.isMember(jss::method)) { - if (jv.isMember(jss::params) && - !(jv[jss::params].isNull() || jv[jss::params].isArray() || - jv[jss::params].isObject())) - return false; - return true; + return !jv.isMember(jss::params) || + (jv[jss::params].isNull() || jv[jss::params].isArray() || + jv[jss::params].isObject()); } } return false; @@ -556,7 +571,7 @@ private: { Json::Reader reader; Json::Value jv; - bool valid_parse = reader.parse(jvParams[0u].asString(), jv); + bool const valid_parse = reader.parse(jvParams[0u].asString(), jv); if (valid_parse && isValidJson2(jv)) { if (jv.isObject()) @@ -603,11 +618,12 @@ private: // ledger [id|index|current|closed|validated] [full|tx] Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseLedger(Json::Value const& jvParams) { Json::Value jvRequest(Json::objectValue); - if (!jvParams.size()) + if (jvParams.size() == 0u) { return jvRequest; } @@ -632,11 +648,12 @@ private: // ledger_header | Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseLedgerId(Json::Value const& jvParams) { Json::Value jvRequest(Json::objectValue); - std::string strLedger = jvParams[0u].asString(); + std::string const strLedger = jvParams[0u].asString(); if (strLedger.length() == 64) { @@ -652,6 +669,7 @@ private: // ledger_entry [id] [] Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseLedgerEntry(Json::Value const& jvParams) { Json::Value jvRequest{Json::objectValue}; @@ -669,6 +687,7 @@ private: // specified severity log_level : Set specified // partition to specified severity Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseLogLevel(Json::Value const& jvParams) { Json::Value jvRequest(Json::objectValue); @@ -717,6 +736,7 @@ private: // channel_authorize: [] Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseChannelAuthorize(Json::Value const& jvParams) { Json::Value jvRequest(Json::objectValue); @@ -760,6 +780,7 @@ private: // channel_verify Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseChannelVerify(Json::Value const& jvParams) { std::string const strPk = jvParams[0u].asString(); @@ -788,6 +809,7 @@ private: } Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseAccountRaw2(Json::Value const& jvParams, char const* const acc2Field) { std::array accFields{{jss::account, acc2Field}}; @@ -795,7 +817,8 @@ private: Json::Value jvRequest(Json::objectValue); for (auto i = 0; i < nParams; ++i) { - std::string strParam = jvParams[i].asString(); + // This was non-const. see comment below + std::string const strParam = jvParams[i].asString(); if (i == 1 && strParam.empty()) continue; @@ -805,7 +828,10 @@ private: { if (parseBase58(strParam)) { - jvRequest[accFields[i]] = std::move(strParam); + // TODO: this was std::move'd before but it does not work in practice. + // We would need a Value(std::string&&) for it to work. + // See https://github.com/XRPLF/rippled/issues/6677 + jvRequest[accFields[i]] = strParam; } else { @@ -825,10 +851,11 @@ private: // TODO: Get index from an alternate syntax: rXYZ: Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseAccountRaw1(Json::Value const& jvParams) { - std::string strIdent = jvParams[0u].asString(); - unsigned int iCursor = jvParams.size(); + std::string const strIdent = jvParams[0u].asString(); + unsigned int const iCursor = jvParams.size(); if (!parseBase58(strIdent)) return rpcError(rpcACT_MALFORMED); @@ -845,9 +872,10 @@ private: } Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseVault(Json::Value const& jvParams) { - std::string strVaultID = jvParams[0u].asString(); + std::string const strVaultID = jvParams[0u].asString(); uint256 id = beast::zero; if (!id.parseHex(strVaultID)) return rpcError(rpcINVALID_PARAMS); @@ -863,6 +891,7 @@ private: // peer_reservations_add [] Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parsePeerReservationsAdd(Json::Value const& jvParams) { Json::Value jvRequest; @@ -876,6 +905,7 @@ private: // peer_reservations_del Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parsePeerReservationsDel(Json::Value const& jvParams) { Json::Value jvRequest; @@ -889,7 +919,7 @@ private: { Json::Reader reader; Json::Value jvRequest{Json::objectValue}; - bool bLedger = 2 == jvParams.size(); + bool const bLedger = 2 == jvParams.size(); JLOG(j_.trace()) << "RPC json: " << jvParams[0u]; @@ -911,6 +941,7 @@ private: // simulate [binary] // simulate [binary] Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseSimulate(Json::Value const& jvParams) { Json::Value txJSON; @@ -942,6 +973,7 @@ private: // submit // submit Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseSignSubmit(Json::Value const& jvParams) { Json::Value txJSON; @@ -953,7 +985,7 @@ private: return std::nullopt; if (jvParams.size() < 4 && bOffline) return std::nullopt; - Json::UInt index = bOffline ? 3u : 2u; + Json::UInt const index = bOffline ? 3u : 2u; return jvParams[index].asString(); }(); @@ -967,8 +999,7 @@ private: return jvRequest; } - else if ( - (jvParams.size() >= 2 || bOffline) && reader.parse(jvParams[1u].asString(), txJSON)) + if ((jvParams.size() >= 2 || bOffline) && reader.parse(jvParams[1u].asString(), txJSON)) { // Signing or submitting tx_json. Json::Value jvRequest{Json::objectValue}; @@ -992,6 +1023,7 @@ private: // // submit_multisigned Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseSubmitMultiSigned(Json::Value const& jvParams) { if (1 == jvParams.size()) @@ -1011,6 +1043,7 @@ private: // transaction_entry Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseTransactionEntry(Json::Value const& jvParams) { // Parameter count should have already been verified. @@ -1036,6 +1069,7 @@ private: // tx Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseTx(Json::Value const& jvParams) { Json::Value jvRequest{Json::objectValue}; @@ -1055,15 +1089,20 @@ private: } if (jvParams[0u].asString().length() == 16) + { jvRequest[jss::ctid] = jvParams[0u].asString(); + } else + { jvRequest[jss::transaction] = jvParams[0u].asString(); + } return jvRequest; } // tx_history Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseTxHistory(Json::Value const& jvParams) { Json::Value jvRequest{Json::objectValue}; @@ -1080,11 +1119,12 @@ private: // (e.g. .bash_history) and it may be leaked via the process status command // (i.e. ps). Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseValidationCreate(Json::Value const& jvParams) { Json::Value jvRequest{Json::objectValue}; - if (jvParams.size()) + if (jvParams.size() != 0u) jvRequest[jss::secret] = jvParams[0u].asString(); return jvRequest; @@ -1094,11 +1134,12 @@ private: // is only for testing. Master seeds should only be generated // randomly. Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseWalletPropose(Json::Value const& jvParams) { Json::Value jvRequest{Json::objectValue}; - if (jvParams.size()) + if (jvParams.size() != 0u) jvRequest[jss::passphrase] = jvParams[0u].asString(); return jvRequest; @@ -1109,6 +1150,7 @@ private: // ]] Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseGatewayBalances(Json::Value const& jvParams) { unsigned int index = 0; @@ -1123,9 +1165,13 @@ private: if (param[0] != 'r') { if (param.size() == 64) + { jvRequest[jss::ledger_hash] = param; + } else + { jvRequest[jss::ledger_index] = param; + } if (size <= index) return RPC::make_param_error("Invalid hotwallet"); @@ -1147,6 +1193,7 @@ private: // server_definitions [hash] Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseServerDefinitions(Json::Value const& jvParams) { Json::Value jvRequest{Json::objectValue}; @@ -1161,6 +1208,7 @@ private: // server_info [counters] Json::Value + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) parseServerInfo(Json::Value const& jvParams) { Json::Value jvRequest(Json::objectValue); @@ -1355,10 +1403,12 @@ struct RPCCallImp // Receive reply if (strData.empty()) + { Throw( "no response from server. Please " - "ensure that the rippled server is running in another " + "ensure that the xrpld server is running in another " "process."); + } // Parse reply JLOG(j.debug()) << "RPC reply: " << strData << std::endl; @@ -1435,9 +1485,13 @@ rpcCmdToJson( }; if (jvRequest.isObject()) + { insert_api_version(jvRequest); + } else if (jvRequest.isArray()) + { std::for_each(jvRequest.begin(), jvRequest.end(), insert_api_version); + } JLOG(j.trace()) << "RPC Request: " << jvRequest << std::endl; return jvRequest; @@ -1476,8 +1530,8 @@ rpcClient( xrpl::ServerHandler::Setup setup; try { - setup = setup_ServerHandler( - config, beast::logstream{logs.journal("HTTPClient").warn()}); + beast::logstream rpcCallLog{logs.journal("HTTPClient").warn()}; + setup = setup_ServerHandler(config, rpcCallLog); } catch (std::exception const&) // NOLINT(bugprone-empty-catch) { @@ -1500,7 +1554,9 @@ rpcClient( jvRequest["admin_password"] = setup.client.admin_password; if (jvRequest.isObject()) + { jvParams.append(jvRequest); + } else if (jvRequest.isArray()) { for (Json::UInt i = 0; i < jvRequest.size(); ++i) @@ -1516,11 +1572,14 @@ rpcClient( setup.client.user, setup.client.password, "", - jvRequest.isMember(jss::method) // Allow parser to rewrite method. - ? jvRequest[jss::method].asString() - : jvRequest.isArray() ? "batch" : args[0], - jvParams, // Parsed, execute. - setup.client.secure != 0, // Use SSL + // Allow parser to rewrite method. + [&]() -> std::string { + if (jvRequest.isMember(jss::method)) + return jvRequest[jss::method].asString(); + return jvRequest.isArray() ? "batch" : args[0]; + }(), + jvParams, // Parsed, execute. + static_cast(setup.client.secure) != 0, // Use SSL config.quiet(), logs, std::bind(RPCCallImp::callRPCHandler, &jvOutput, std::placeholders::_1), @@ -1539,7 +1598,7 @@ rpcClient( else { // Transport error. - Json::Value jvRpcError = jvOutput; + Json::Value const jvRpcError = jvOutput; jvOutput = rpcError(rpcJSON_RPC); jvOutput["result"] = jvRpcError; @@ -1557,11 +1616,17 @@ rpcClient( { jvOutput[jss::status] = "error"; if (jvOutput.isMember(jss::error_code)) + { nRet = std::stoi(jvOutput[jss::error_code].asString()); + } else if (jvOutput[jss::error].isMember(jss::error_code)) + { nRet = std::stoi(jvOutput[jss::error][jss::error_code].asString()); + } else + { nRet = rpcBAD_SYNTAX; + } } // YYY We could have a command line flag for single line output for diff --git a/src/xrpld/rpc/detail/RPCHandler.cpp b/src/xrpld/rpc/detail/RPCHandler.cpp index 2066c8a5e9..1d8e1168b4 100644 --- a/src/xrpld/rpc/detail/RPCHandler.cpp +++ b/src/xrpld/rpc/detail/RPCHandler.cpp @@ -129,7 +129,7 @@ fillHandler(JsonContext& context, Handler const*& result) return rpcUNKNOWN_COMMAND; } - std::string strCommand = context.params.isMember(jss::command) + std::string const strCommand = context.params.isMember(jss::command) ? context.params[jss::command].asString() : context.params[jss::method].asString(); @@ -137,13 +137,13 @@ fillHandler(JsonContext& context, Handler const*& result) JLOG(context.j.trace()) << "REQUEST:" << context.params; auto handler = getHandler(context.apiVersion, context.app.config().BETA_RPC_API, strCommand); - if (!handler) + if (handler == nullptr) return rpcUNKNOWN_COMMAND; if (handler->role_ == Role::ADMIN && context.role != Role::ADMIN) return rpcNO_PERMISSION; - error_code_i res = conditionMet(handler->condition_, context); + error_code_i const res = conditionMet(handler->condition_, context); if (res != rpcSUCCESS) { return res; @@ -215,11 +215,9 @@ doCommand(RPC::JsonContext& context, Json::Value& result) return ret; } - else - { - auto ret = callMethod(context, method, handler->name_, result); - return ret; - } + + auto ret = callMethod(context, method, handler->name_, result); + return ret; } return rpcUNKNOWN_COMMAND; @@ -230,7 +228,7 @@ roleRequired(unsigned int version, bool betaEnabled, std::string const& method) { auto handler = RPC::getHandler(version, betaEnabled, method); - if (!handler) + if (handler == nullptr) return Role::FORBID; return handler->role_; diff --git a/src/xrpld/rpc/detail/RPCHelpers.cpp b/src/xrpld/rpc/detail/RPCHelpers.cpp index 385bc7f4af..190254fc37 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.cpp +++ b/src/xrpld/rpc/detail/RPCHelpers.cpp @@ -1,16 +1,16 @@ #include -#include #include #include #include +#include #include +#include #include #include #include #include #include -#include #include #include @@ -24,9 +24,13 @@ getStartHint(std::shared_ptr const& sle, AccountID const& accountID) if (sle->getType() == ltRIPPLE_STATE) { if (sle->getFieldAmount(sfLowLimit).getIssuer() == accountID) + { return sle->getFieldU64(sfLowNode); - else if (sle->getFieldAmount(sfHighLimit).getIssuer() == accountID) + } + if (sle->getFieldAmount(sfHighLimit).getIssuer() == accountID) + { return sle->getFieldU64(sfHighNode); + } } if (!sle->isFieldPresent(sfOwnerNode)) @@ -46,7 +50,7 @@ isRelatedToAccount( return (sle->getFieldAmount(sfLowLimit).getIssuer() == accountID) || (sle->getFieldAmount(sfHighLimit).getIssuer() == accountID); } - else if (sle->isFieldPresent(sfAccount)) + if (sle->isFieldPresent(sfAccount)) { // If there's an sfAccount present, also test the sfDestination, if // present. This will match objects such as Escrows (ltESCROW), Payment @@ -57,12 +61,12 @@ isRelatedToAccount( return sle->getAccountID(sfAccount) == accountID || (sle->isFieldPresent(sfDestination) && sle->getAccountID(sfDestination) == accountID); } - else if (sle->getType() == ltSIGNER_LIST) + if (sle->getType() == ltSIGNER_LIST) { Keylet const accountSignerList = keylet::signers(accountID); return sle->key() == accountSignerList.key; } - else if (sle->getType() == ltNFTOKEN_OFFER) + if (sle->getType() == ltNFTOKEN_OFFER) { // Do not check the sfDestination field. NFToken Offers are NOT added to // the Destination account's directory. @@ -96,7 +100,7 @@ readLimitField(unsigned int& limit, Tuning::LimitRange const& range, JsonContext return std::nullopt; auto const& jvLimit = context.params[jss::limit]; - if (!(jvLimit.isUInt() || (jvLimit.isInt() && jvLimit.asInt() >= 0))) + if (!jvLimit.isUInt() && (!jvLimit.isInt() || jvLimit.asInt() < 0)) return RPC::expected_field_error(jss::limit, "unsigned integer"); limit = jvLimit.asUInt(); @@ -110,10 +114,10 @@ readLimitField(unsigned int& limit, Tuning::LimitRange const& range, JsonContext } std::optional -parseRippleLibSeed(Json::Value const& value) +parseXrplLibSeed(Json::Value const& value) { - // ripple-lib encodes seed used to generate an Ed25519 wallet in a - // non-standard way. While rippled never encode seeds that way, we + // XrplLib encodes seed used to generate an Ed25519 wallet in a + // non-standard way. While xrpld never encode seeds that way, we // try to detect such keys to avoid user confusion. if (!value.isString()) return std::nullopt; @@ -234,9 +238,13 @@ keypairForSignature(Json::Value const& params, Json::Value& error, unsigned int if (!keyType) { if (apiVersion > 1u) + { error = RPC::make_error(rpcBAD_KEY_TYPE); + } else + { error = RPC::invalid_field_error(jss::key_type); + } return {}; } @@ -250,14 +258,14 @@ keypairForSignature(Json::Value const& params, Json::Value& error, unsigned int } } - // ripple-lib encodes seed used to generate an Ed25519 wallet in a + // XrplLib encodes seed used to generate an Ed25519 wallet in a // non-standard way. While we never encode seeds that way, we try // to detect such keys to avoid user confusion. // using strcmp as pointers may not match (see // https://developercommunity.visualstudio.com/t/assigning-constexpr-char--to-static-cha/10021357?entry=problem) if (strcmp(secretType, jss::seed_hex.c_str()) != 0) { - seed = RPC::parseRippleLibSeed(params[secretType]); + seed = RPC::parseXrplLibSeed(params[secretType]); if (seed) { @@ -279,7 +287,9 @@ keypairForSignature(Json::Value const& params, Json::Value& error, unsigned int if (!seed) { if (has_key_type) + { seed = getSeedFromRPC(params, error); + } else { if (!params[jss::secret].isString()) diff --git a/src/xrpld/rpc/detail/RPCHelpers.h b/src/xrpld/rpc/detail/RPCHelpers.h index 54a976b78e..00a0018870 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.h +++ b/src/xrpld/rpc/detail/RPCHelpers.h @@ -94,16 +94,16 @@ std::optional getSeedFromRPC(Json::Value const& params, Json::Value& error); /** - * @brief Parses a RippleLib seed from RPC parameters. + * @brief Parses a XrplLib seed from RPC parameters. * * Attempts to extract and return a Seed from the provided JSON parameters using - * RippleLib conventions. + * XrplLib conventions. * * @param params The JSON value containing RPC parameters. * @return An optional Seed if parsing is successful, or std::nullopt otherwise. */ std::optional -parseRippleLibSeed(Json::Value const& params); +parseXrplLibSeed(Json::Value const& params); /** * @brief Chooses the ledger entry type based on RPC parameters. diff --git a/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp b/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp index ddf78425bd..955533c776 100644 --- a/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp +++ b/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp @@ -55,7 +55,7 @@ ledgerFromIndex( if (index == "closed") return getLedger(ledger, LedgerShortcut::Closed, context); - std::uint32_t iVal; + std::uint32_t iVal = 0; if (!beast::lexicalCastChecked(iVal, index)) return {rpcINVALID_PARAMS, expected_field_message(fieldName, "string or number")}; @@ -78,10 +78,12 @@ ledgerFromRequest(T& ledger, JsonContext const& context) // while `ledger` is still supported, it is deprecated // and therefore shouldn't be mentioned in the error message if (hasLedger) + { return { rpcINVALID_PARAMS, "Exactly one of 'ledger', 'ledger_hash', or " "'ledger_index' can be specified."}; + } return { rpcINVALID_PARAMS, "Exactly one of 'ledger_hash' or " @@ -97,9 +99,11 @@ ledgerFromRequest(T& ledger, JsonContext const& context) return {rpcINVALID_PARAMS, expected_field_message(jss::ledger, "string or number")}; } if (legacyLedger.isString() && legacyLedger.asString().size() == 64) + { return ledgerFromHash(ledger, legacyLedger, context, jss::ledger); - else - return ledgerFromIndex(ledger, legacyLedger, context, jss::ledger); + } + + return ledgerFromIndex(ledger, legacyLedger, context, jss::ledger); } if (hasHash) @@ -162,7 +166,7 @@ ledgerFromSpecifier( ledger.reset(); using LedgerCase = org::xrpl::rpc::v1::LedgerSpecifier::LedgerCase; - LedgerCase ledgerCase = specifier.ledger_case(); + LedgerCase const ledgerCase = specifier.ledger_case(); switch (ledgerCase) { case LedgerCase::kHash: { @@ -182,17 +186,15 @@ ledgerFromSpecifier( { return getLedger(ledger, LedgerShortcut::Validated, context); } - else + + if (shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT || + shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_UNSPECIFIED) { - if (shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT || - shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_UNSPECIFIED) - { - return getLedger(ledger, LedgerShortcut::Current, context); - } - else if (shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED) - { - return getLedger(ledger, LedgerShortcut::Closed, context); - } + return getLedger(ledger, LedgerShortcut::Current, context); + } + if (shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED) + { + return getLedger(ledger, LedgerShortcut::Closed, context); } } } @@ -373,7 +375,7 @@ getOrAcquireLedger(RPC::JsonContext const& context) auto& ledgerMaster = context.app.getLedgerMaster(); LedgerHash ledgerHash; - if ((hasHash + hasIndex) != 1) + if ((static_cast(hasHash) + static_cast(hasIndex)) != 1) { return Unexpected( RPC::make_param_error( @@ -409,7 +411,7 @@ getOrAcquireLedger(RPC::JsonContext const& context) if (ledgerIndex <= 0) return Unexpected(RPC::make_param_error("Ledger index too small")); - auto const j = context.app.journal("RPCHandler"); + auto const j = context.app.getJournal("RPCHandler"); // Try to get the hash of the desired ledger from the validated // ledger auto neededHash = hashOfSeq(*ledger, ledgerIndex, j); diff --git a/src/xrpld/rpc/detail/RPCLedgerHelpers.h b/src/xrpld/rpc/detail/RPCLedgerHelpers.h index c60b7bc6b2..6a04ab41d6 100644 --- a/src/xrpld/rpc/detail/RPCLedgerHelpers.h +++ b/src/xrpld/rpc/detail/RPCLedgerHelpers.h @@ -1,11 +1,11 @@ #pragma once -#include #include #include #include #include +#include #include #include #include diff --git a/src/xrpld/rpc/detail/RPCSub.cpp b/src/xrpld/rpc/detail/RPCSub.cpp index a0e29f68e2..3b5b56d937 100644 --- a/src/xrpld/rpc/detail/RPCSub.cpp +++ b/src/xrpld/rpc/detail/RPCSub.cpp @@ -21,31 +21,42 @@ public: std::string const& strUrl, std::string const& strUsername, std::string const& strPassword, - Logs& logs) + ServiceRegistry& registry) : RPCSub(source) , m_io_context(io_context) , m_jobQueue(jobQueue) , mUrl(strUrl) - , mSSL(false) , mUsername(strUsername) , mPassword(strPassword) - , mSending(false) - , j_(logs.journal("RPCSub")) - , logs_(logs) + , j_(registry.getJournal("RPCSub")) + , logs_(registry.getLogs()) { parsedURL pUrl; if (!parseUrl(pUrl, strUrl)) + { Throw("Failed to parse url."); + } else if (pUrl.scheme == "https") + { mSSL = true; + } else if (pUrl.scheme != "http") + { Throw("Only http and https is supported."); + } mSeq = 1; mIp = pUrl.domain; - mPort = (!pUrl.port) ? (mSSL ? 443 : 80) : *pUrl.port; + if (!pUrl.port) + { + mPort = mSSL ? 443 : 80; + } + else + { + mPort = *pUrl.port; + } mPath = pUrl.path; JLOG(j_.info()) << "RPCCall::fromNetwork sub: ip=" << mIp << " port=" << mPort @@ -57,7 +68,7 @@ public: void send(Json::Value const& jvObj, bool broadcast) override { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); auto jm = broadcast ? j_.debug() : j_.info(); JLOG(jm) << "RPCCall::fromNetwork push: " << jvObj; @@ -77,7 +88,7 @@ public: void setUsername(std::string const& strUsername) override { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); mUsername = strUsername; } @@ -85,7 +96,7 @@ public: void setPassword(std::string const& strPassword) override { - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); mPassword = strPassword; } @@ -97,13 +108,13 @@ private: sendThread() { Json::Value jvEvent; - bool bSend; + bool bSend = false; do { { // Obtain the lock to manipulate the queue and change sending. - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); if (mDeque.empty()) { @@ -159,14 +170,14 @@ private: std::string mUrl; std::string mIp; std::uint16_t mPort; - bool mSSL; + bool mSSL{false}; std::string mUsername; std::string mPassword; std::string mPath; int mSeq; // Next id to allocate. - bool mSending; // Sending thread is active. + bool mSending{false}; // Sending thread is active. std::deque> mDeque; @@ -188,7 +199,7 @@ make_RPCSub( std::string const& strUrl, std::string const& strUsername, std::string const& strPassword, - Logs& logs) + ServiceRegistry& registry) { return std::make_shared( std::ref(source), @@ -197,7 +208,7 @@ make_RPCSub( strUrl, strUsername, strPassword, - logs); + registry); } } // namespace xrpl diff --git a/src/xrpld/app/paths/RippleLineCache.cpp b/src/xrpld/rpc/detail/RippleLineCache.cpp similarity index 95% rename from src/xrpld/app/paths/RippleLineCache.cpp rename to src/xrpld/rpc/detail/RippleLineCache.cpp index 9916facdc2..7cc77f9b8b 100644 --- a/src/xrpld/app/paths/RippleLineCache.cpp +++ b/src/xrpld/rpc/detail/RippleLineCache.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include namespace xrpl { @@ -26,7 +26,7 @@ RippleLineCache::getRippleLines(AccountID const& accountID, LineDirection direct direction == LineDirection::outgoing ? LineDirection::incoming : LineDirection::outgoing, hash); - std::lock_guard sl(mLock); + std::lock_guard const sl(mLock); auto [it, inserted] = [&]() { @@ -76,7 +76,7 @@ RippleLineCache::getRippleLines(AccountID const& accountID, LineDirection direct { XRPL_ASSERT(it->second == nullptr, "xrpl::RippleLineCache::getRippleLines : null lines"); auto lines = PathFindTrustLine::getItems(accountID, *ledger_, direction); - if (lines.size()) + if (!lines.empty()) { it->second = std::make_shared>(std::move(lines)); totalLineCount_ += it->second->size(); @@ -84,7 +84,7 @@ RippleLineCache::getRippleLines(AccountID const& accountID, LineDirection direct } XRPL_ASSERT( - !it->second || (it->second->size() > 0), + !it->second || (!it->second->empty()), "xrpl::RippleLineCache::getRippleLines : null or nonempty lines"); auto const size = it->second ? it->second->size() : 0; JLOG(journal_.trace()) << "getRippleLines for ledger " << ledger_->header().seq << " found " diff --git a/src/xrpld/app/paths/RippleLineCache.h b/src/xrpld/rpc/detail/RippleLineCache.h similarity index 97% rename from src/xrpld/app/paths/RippleLineCache.h rename to src/xrpld/rpc/detail/RippleLineCache.h index c4ddad6c81..65607f2d25 100644 --- a/src/xrpld/app/paths/RippleLineCache.h +++ b/src/xrpld/rpc/detail/RippleLineCache.h @@ -1,10 +1,10 @@ #pragma once -#include -#include +#include #include #include +#include #include #include diff --git a/src/xrpld/rpc/detail/Role.cpp b/src/xrpld/rpc/detail/Role.cpp index e040a55c69..f832e43119 100644 --- a/src/xrpld/rpc/detail/Role.cpp +++ b/src/xrpld/rpc/detail/Role.cpp @@ -83,7 +83,7 @@ requestRole( if (ipAllowed(remoteIp.address(), port.secure_gateway_nets_v4, port.secure_gateway_nets_v6)) { - if (user.size()) + if (!user.empty()) return Role::IDENTIFIED; return Role::PROXY; } @@ -137,9 +137,11 @@ extractIpAddrFromField(std::string_view field) { std::size_t const firstNonSpace = ret.find_first_not_of(' '); if (firstNonSpace == std::string_view::npos) + { // We know there's at least one leading space. So if we got // npos, then it must be all spaces. Return empty string_view. return {}; + } ret = ret.substr(firstNonSpace); } @@ -151,9 +153,11 @@ extractIpAddrFromField(std::string_view field) { std::size_t const lastNonSpace = ret.find_last_not_of(" \r\n"); if (lastNonSpace == std::string_view::npos) + { // We know there's at least one leading space. So if we // got npos, then it must be all spaces. return {}; + } ret = ret.substr(0, lastNonSpace + 1); } @@ -220,7 +224,7 @@ extractIpAddrFromField(std::string_view field) // If there's a port appended to the IP address, strip that by // terminating at the colon. - if (std::size_t colon = ret.find(':'); colon != std::string_view::npos) + if (std::size_t const colon = ret.find(':'); colon != std::string_view::npos) ret = ret.substr(0, colon); return ret; @@ -252,7 +256,7 @@ forwardedFor(http_request_type const& request) // We found a "for=". Scan for the end of the IP address. std::size_t const pos = [&found, &it]() { - std::size_t pos = + std::size_t const pos = std::string_view(found, it->value().end() - found).find_first_of(",;"); if (pos != std::string_view::npos) return pos; diff --git a/src/xrpld/rpc/detail/ServerHandler.cpp b/src/xrpld/rpc/detail/ServerHandler.cpp index 5be41a5a23..e5cc7a83bf 100644 --- a/src/xrpld/rpc/detail/ServerHandler.cpp +++ b/src/xrpld/rpc/detail/ServerHandler.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -79,12 +78,12 @@ authorized(Port const& port, std::map const& h) return false; std::string strUserPass64 = it->second.substr(6); boost::trim(strUserPass64); - std::string strUserPass = base64_decode(strUserPass64); - std::string::size_type nColon = strUserPass.find(":"); + std::string const strUserPass = base64_decode(strUserPass64); + std::string::size_type const nColon = strUserPass.find(':'); if (nColon == std::string::npos) return false; - std::string strUser = strUserPass.substr(0, nColon); - std::string strPassword = strUserPass.substr(nColon + 1); + std::string const strUser = strUserPass.substr(0, nColon); + std::string const strPassword = strUserPass.substr(nColon + 1); return strUser == port.user && strPassword == port.password; } @@ -98,9 +97,9 @@ ServerHandler::ServerHandler( CollectorManager& cm) : app_(app) , m_resourceManager(resourceManager) - , m_journal(app_.journal("Server")) + , m_journal(app_.getJournal("Server")) , m_networkOPs(networkOPs) - , m_server(make_Server(*this, io_context, app_.journal("Server"))) + , m_server(make_Server(*this, io_context, app_.getJournal("Server"))) , m_jobQueue(jobQueue) { auto const& group(cm.group("rpc")); @@ -126,14 +125,14 @@ ServerHandler::setup(Setup const& setup, beast::Journal journal) if (auto it = endpoints_.find(port.name); it != endpoints_.end()) { auto const endpointPort = it->second.port(); - if (!port.port) + if (port.port == 0u) port.port = endpointPort; - if (!setup_.client.port && + if ((setup_.client.port == 0u) && (port.protocol.count("http") > 0 || port.protocol.count("https") > 0)) setup_.client.port = endpointPort; - if (!setup_.overlay.port() && (port.protocol.count("peer") > 0)) + if ((setup_.overlay.port() == 0u) && (port.protocol.count("peer") > 0)) setup_.overlay.port(endpointPort); } } @@ -159,11 +158,11 @@ ServerHandler::onAccept(Session& session, boost::asio::ip::tcp::endpoint endpoin auto const& port = session.port(); auto const c = [this, &port]() { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); return ++count_[port]; }(); - if (port.limit && c >= port.limit) + if ((port.limit != 0) && c >= port.limit) { JLOG(m_journal.trace()) << port.name << " is full; dropping " << endpoint; return false; @@ -218,7 +217,7 @@ ServerHandler::onHandoff( } if (bundle && p.count("peer") > 0) - return app_.overlay().onHandoff(std::move(bundle), std::move(request), remote_address); + return app_.getOverlay().onHandoff(std::move(bundle), std::move(request), remote_address); if (is_ws && isStatusRequest(request)) return statusResponse(request); @@ -270,7 +269,7 @@ 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) { - HTTPReply(403, "Forbidden", makeOutput(session), app_.journal("RPC")); + HTTPReply(403, "Forbidden", makeOutput(session), app_.getJournal("RPC")); session.close(true); return; } @@ -278,12 +277,12 @@ ServerHandler::onRequest(Session& session) // Check user/password authorization if (!authorized(session.port(), build_map(session.request()))) { - HTTPReply(403, "Forbidden", makeOutput(session), app_.journal("RPC")); + HTTPReply(403, "Forbidden", makeOutput(session), app_.getJournal("RPC")); session.close(true); return; } - std::shared_ptr detachedSession = session.detach(); + std::shared_ptr const detachedSession = session.detach(); auto const postResult = m_jobQueue.postCoro( jtCLIENT_RPC, "RPC-Client", [this, detachedSession](std::shared_ptr coro) { processSession(detachedSession, coro); @@ -291,7 +290,7 @@ ServerHandler::onRequest(Session& session) if (postResult == nullptr) { // The coroutine was rejected, probably because we're shutting down. - HTTPReply(503, "Service Unavailable", makeOutput(*detachedSession), app_.journal("RPC")); + HTTPReply(503, "Service Unavailable", makeOutput(*detachedSession), app_.getJournal("RPC")); detachedSession->close(true); return; } @@ -344,14 +343,14 @@ ServerHandler::onWSMessage( void ServerHandler::onClose(Session& session, boost::system::error_code const&) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); --count_[session.port()]; } void ServerHandler::onStopped(Server&) { - std::lock_guard lock(mutex_); + std::lock_guard const lock(mutex_); stopped_ = true; condition_.notify_one(); } @@ -363,9 +362,13 @@ void logDuration(Json::Value const& request, T const& duration, beast::Journal& journal) { using namespace std::chrono_literals; - auto const level = (duration >= 10s) ? journal.error() - : (duration >= 1s) ? journal.warn() - : journal.debug(); + auto const level = [&]() { + if (duration >= 10s) + return journal.error(); + if (duration >= 1s) + return journal.warn(); + return journal.debug(); + }(); JLOG(level) << "RPC request processing duration = " << std::chrono::duration_cast(duration).count() @@ -436,7 +439,7 @@ ServerHandler::processSession( else { RPC::JsonContext context{ - {app_.journal("RPCHandler"), + {app_.getJournal("RPCHandler"), app_, loadType, app_.getOPs(), @@ -535,9 +538,13 @@ ServerHandler::processSession( }()); if (beast::rfc2616::is_keep_alive(session->request())) + { session->complete(); + } else + { session->close(true); + } } static Json::Value @@ -561,12 +568,12 @@ ServerHandler::processRequest( Port const& port, std::string const& request, beast::IP::Endpoint const& remoteIPAddress, - Output&& output, + Output const& output, std::shared_ptr coro, std::string_view forwardedFor, std::string_view user) { - auto rpcJ = app_.journal("RPC"); + auto rpcJ = app_.getJournal("RPC"); Json::Value jsonOrig; { @@ -643,8 +650,10 @@ ServerHandler::processRequest( auto role = Role::FORBID; auto required = Role::FORBID; if (jsonRPC.isMember(jss::method) && jsonRPC[jss::method].isString()) + { required = RPC::roleRequired( apiVersion, app_.config().BETA_RPC_API, jsonRPC[jss::method].asString()); + } if (jsonRPC.isMember(jss::params) && jsonRPC[jss::params].isArray() && jsonRPC[jss::params].size() > 0 && jsonRPC[jss::params][Json::UInt(0)].isObjectOrNull()) @@ -723,7 +732,7 @@ ServerHandler::processRequest( continue; } - std::string strMethod = method.asString(); + std::string const strMethod = method.asString(); if (strMethod.empty()) { usage.charge(Resource::feeMalformedRPC); @@ -749,8 +758,9 @@ ServerHandler::processRequest( { params = jsonRPC[jss::params]; if (!params) + { params = Json::Value(Json::objectValue); - + } else if (!params.isArray() || params.size() != 1) { usage.charge(Resource::feeMalformedRPC); @@ -909,9 +919,13 @@ ServerHandler::processRequest( if (params.isMember(jss::id)) r[jss::id] = params[jss::id]; if (batch) + { reply.append(std::move(r)); + } else + { reply = std::move(r); + } if (reply.isMember(jss::result) && reply[jss::result].isMember(jss::result)) { @@ -957,9 +971,13 @@ ServerHandler::processRequest( { static int const maxSize = 10000; if (response.size() <= maxSize) + { stream << "Reply: " << response; + } else + { stream << "Reply: " << response.substr(0, maxSize); + } } HTTPReply(httpStatus, response, output, rpcJ); @@ -981,10 +999,9 @@ ServerHandler::statusResponse(http_request_type const& request) const if (app_.serverOkay(reason)) { msg.result(boost::beast::http::status::ok); - msg.body() = "" + systemName() + - " Test page for rippled

" + systemName() + - " Test

This page shows rippled http(s) " - "connectivity is working.

"; + msg.body() = "Test page for " + systemName() + + "

Test

This page shows " + systemName() + + " http(s) connectivity is working.

"; } else { @@ -1010,10 +1027,14 @@ ServerHandler::Setup::makeContexts() if (p.secure()) { if (p.ssl_key.empty() && p.ssl_cert.empty() && p.ssl_chain.empty()) + { p.context = make_SSLContext(p.ssl_ciphers); + } else + { p.context = make_SSLContextAuthed(p.ssl_key, p.ssl_cert, p.ssl_chain, p.ssl_ciphers); + } } else { @@ -1113,10 +1134,14 @@ parse_Ports(Config const& config, std::ostream& log) // Remove the peer protocol, and if that would // leave the port empty, remove the port as well - if (p.erase("peer") && p.empty()) + if ((p.erase("peer") != 0u) && p.empty()) + { it = result.erase(it); + } else + { ++it; + } } } else @@ -1144,15 +1169,22 @@ setup_Client(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) break; + } if (iter == setup.ports.cend()) return; setup.client.secure = iter->protocol.count("https") > 0; - setup.client.ip = beast::IP::is_unspecified(iter->ip) ? - // VFALCO HACK! to make localhost work - (iter->ip.is_v6() ? "::1" : "127.0.0.1") - : iter->ip.to_string(); + if (beast::IP::is_unspecified(iter->ip)) + { + // VFALCO HACK! to make localhost work + setup.client.ip = iter->ip.is_v6() ? "::1" : "127.0.0.1"; + } + else + { + setup.client.ip = iter->ip.to_string(); + } setup.client.port = iter->port; setup.client.user = iter->user; setup.client.password = iter->password; @@ -1176,7 +1208,7 @@ setup_Overlay(ServerHandler::Setup& setup) } ServerHandler::Setup -setup_ServerHandler(Config const& config, std::ostream&& log) +setup_ServerHandler(Config const& config, std::ostream& log) { ServerHandler::Setup setup; setup.ports = parse_Ports(config, log); diff --git a/src/xrpld/rpc/detail/TransactionSign.cpp b/src/xrpld/rpc/detail/TransactionSign.cpp index 8afe3612fe..23c70b9def 100644 --- a/src/xrpld/rpc/detail/TransactionSign.cpp +++ b/src/xrpld/rpc/detail/TransactionSign.cpp @@ -3,12 +3,11 @@ #include #include #include -#include #include +#include #include #include -#include #include #include #include @@ -72,14 +71,14 @@ public: bool validMultiSign() const { - return isMultiSigning() && multiSignPublicKey_ && multiSignature_.size(); + return isMultiSigning() && multiSignPublicKey_ && !multiSignature_.empty(); } // Don't call this method unless isMultiSigning() returns true. AccountID const& getSigner() const { - if (!multiSigningAcctID_) + if (multiSigningAcctID_ == nullptr) LogicError("Accessing unknown SigningForParams::getSigner()"); return *multiSigningAcctID_; } @@ -179,11 +178,15 @@ checkPayment( if (tx_json.isMember(jss::Amount)) { if (tx_json[jss::DeliverMax] != tx_json[jss::Amount]) + { return RPC::make_error( rpcINVALID_PARAMS, "Cannot specify differing 'Amount' and 'DeliverMax'"); + } } else + { tx_json[jss::Amount] = tx_json[jss::DeliverMax]; + } tx_json.removeMember(jss::DeliverMax); } @@ -203,13 +206,17 @@ checkPayment( if (!dstAccountID) return RPC::invalid_field_error("tx_json.Destination"); - if (params.isMember(jss::build_path) && ((doPath == false) || amount.holds())) + if (params.isMember(jss::build_path) && ((!doPath) || amount.holds())) + { return RPC::make_error( rpcINVALID_PARAMS, "Field 'build_path' not allowed in this context."); + } if (tx_json.isMember(jss::Paths) && params.isMember(jss::build_path)) + { return RPC::make_error( rpcINVALID_PARAMS, "Cannot specify both 'tx_json.Paths' and 'build_path'"); + } std::optional domain; if (tx_json.isMember(sfDomainID.jsonName)) @@ -220,10 +227,8 @@ checkPayment( { return RPC::make_error(rpcDOMAIN_MALFORMED, "Unable to parse 'DomainID'."); } - else - { - domain = num; - } + + domain = num; } if (!tx_json.isMember(jss::Paths) && params.isMember(jss::build_path)) @@ -246,16 +251,16 @@ checkPayment( return RPC::make_error(rpcINVALID_PARAMS, "Cannot build XRP to XRP paths."); { - LegacyPathFind lpf(isUnlimited(role), app); + LegacyPathFind const lpf(isUnlimited(role), app); if (!lpf.isOk()) return rpcError(rpcTOO_BUSY); STPathSet result; - if (auto ledger = app.openLedger().current()) + if (auto ledger = app.getOpenLedger().current()) { Pathfinder pf( - std::make_shared(ledger, app.journal("RippleLineCache")), + std::make_shared(ledger, app.getJournal("RippleLineCache")), srcAddressID, *dstAccountID, sendMax.issue().currency, @@ -269,12 +274,12 @@ checkPayment( // 4 is the maximum paths pf.computePathRanks(4); STPath fullLiquidityPath; - STPathSet paths; + STPathSet const paths; result = pf.getBestPaths(4, fullLiquidityPath, paths, sendMax.issue().account); } } - auto j = app.journal("RPCHandler"); + auto j = app.getJournal("RPCHandler"); JLOG(j.debug()) << "transactionSign: build_path: " << result.getJson(JsonOptions::none); if (!result.empty()) @@ -338,9 +343,13 @@ checkTxJsonFields( if (verify && !config.standalone() && (validatedLedgerAge > Tuning::maxValidatedLedgerAge)) { if (apiVersion == 1) + { ret.first = rpcError(rpcNO_CURRENT); + } else + { ret.first = rpcError(rpcNOT_SYNCED); + } return ret; } @@ -392,7 +401,7 @@ transactionPreProcessImpl( std::chrono::seconds validatedLedgerAge, Application& app) { - auto j = app.journal("RPCHandler"); + auto j = app.getJournal("RPCHandler"); Json::Value jvResult; std::optional> keyPair = keypairForSignature(params, jvResult); @@ -418,7 +427,7 @@ transactionPreProcessImpl( : nullptr; if (signatureTarget) { - if (!signatureTemplate) + if (signatureTemplate == nullptr) { // Invalid target field return RPC::make_error(rpcINVALID_PARAMS, signatureTarget->get().getName()); } @@ -451,7 +460,7 @@ transactionPreProcessImpl( std::shared_ptr sle; if (verify) - sle = app.openLedger().current()->read(keylet::account(srcAddressID)); + sle = app.getOpenLedger().current()->read(keylet::account(srcAddressID)); if (verify && !sle) { @@ -525,8 +534,10 @@ transactionPreProcessImpl( if (verify) { if (!sle) + { // XXX Ignore transactions for accounts not created. return rpcError(rpcSRC_ACT_NOT_FOUND); + } JLOG(j.trace()) << "verify: " << toBase58(calcAccountID(pk)) << " : " << toBase58(srcAddressID); @@ -553,7 +564,7 @@ transactionPreProcessImpl( auto delegatedAddressID = *ptrDelegatedAddressID; auto delegatedSle = - app.openLedger().current()->read(keylet::account(delegatedAddressID)); + app.getOpenLedger().current()->read(keylet::account(delegatedAddressID)); if (!delegatedSle) return rpcError(rpcDELEGATE_ACT_NOT_FOUND); @@ -592,8 +603,10 @@ transactionPreProcessImpl( { // If the target object doesn't exist, make one. if (!parsed.object->isFieldPresent(*signatureTarget)) + { parsed.object->setFieldObject( *signatureTarget, STObject{*signatureTemplate, *signatureTarget}); + } sigObject = &parsed.object->peekFieldObject(*signatureTarget); } sigObject->setFieldVL( @@ -618,7 +631,7 @@ transactionPreProcessImpl( // If multisign then return multiSignature, else set TxnSignature field. if (signingArgs.isMultiSigning()) { - Serializer s = buildMultiSigningData(*stTx, signingArgs.getSigner()); + Serializer const s = buildMultiSigningData(*stTx, signingArgs.getSigner()); auto multisig = xrpl::sign(pk, sk, s.slice()); @@ -660,14 +673,16 @@ transactionConstructImpl( { Serializer s; tpTrans->getSTransaction()->add(s); - Blob transBlob = s.getData(); + Blob const transBlob = s.getData(); SerialIter sit{makeSlice(transBlob)}; // Check the signature if that's called for. auto sttxNew = std::make_shared(sit); if (!app.checkSigs()) + { forceValidity( app.getHashRouter(), sttxNew->getTransactionID(), Validity::SigGoodOnly); + } if (checkValidity(app.getHashRouter(), *sttxNew, rules).first != Validity::Valid) { ret.first = RPC::make_error(rpcINTERNAL, "Invalid signature."); @@ -714,7 +729,9 @@ transactionFormatResultImpl(Transaction::pointer tpTrans, unsigned apiVersion) jvResult[jss::hash] = to_string(tpTrans->getID()); } else + { jvResult[jss::tx_json] = tpTrans->getJson(JsonOptions::none); + } RPC::insertDeliverMax( jvResult[jss::tx_json], tpTrans->getSTransaction()->getTxnType(), apiVersion); @@ -747,7 +764,7 @@ transactionFormatResultImpl(Transaction::pointer tpTrans, unsigned apiVersion) [[nodiscard]] static XRPAmount getTxFee(Application const& app, Config const& config, Json::Value tx) { - auto const& ledger = app.openLedger().current(); + auto const& ledger = app.getOpenLedger().current(); // autofilling only needed in this function so that the `STParsedJSONObject` // parsing works properly it should not be modifying the actual `tx` object if (!tx.isMember(jss::Fee)) @@ -809,7 +826,7 @@ getTxFee(Application const& app, Config const& config, Json::Value tx) if (!passesLocalChecks(stTx, reason)) return config.FEES.reference_fee; - return calculateBaseFee(*app.openLedger().current(), stTx); + return calculateBaseFee(*app.getOpenLedger().current(), stTx); } catch (std::exception& e) { @@ -830,7 +847,7 @@ getCurrentNetworkFee( { XRPAmount const feeDefault = getTxFee(app, config, tx); - auto ledger = app.openLedger().current(); + auto ledger = app.getOpenLedger().current(); // Administrative and identified endpoints are exempt from local fees. XRPAmount const loadFee = scaleFeeLoad(feeDefault, feeTrack, ledger->fees(), isUnlimited(role)); XRPAmount fee = loadFee; @@ -880,9 +897,11 @@ checkFee( { mult = request[jss::fee_mult_max].asInt(); if (mult < 0) + { return RPC::make_error( rpcINVALID_PARAMS, RPC::expected_field_message(jss::fee_mult_max, "a positive integer")); + } } else { @@ -896,9 +915,11 @@ checkFee( { div = request[jss::fee_div_max].asInt(); if (div <= 0) + { return RPC::make_error( rpcINVALID_PARAMS, RPC::expected_field_message(jss::fee_div_max, "a positive integer")); + } } else { @@ -928,20 +949,20 @@ transactionSign( { using namespace detail; - auto j = app.journal("RPCHandler"); + auto j = app.getJournal("RPCHandler"); JLOG(j.debug()) << "transactionSign: " << jvRequest; // Add and amend fields based on the transaction type. SigningForParams signForParams; - transactionPreProcessResult preprocResult = + transactionPreProcessResult const preprocResult = transactionPreProcessImpl(jvRequest, role, signForParams, validatedLedgerAge, app); if (!preprocResult.second) return preprocResult.first; - std::shared_ptr ledger = app.openLedger().current(); + std::shared_ptr const ledger = app.getOpenLedger().current(); // Make sure the STTx makes a legitimate Transaction. - std::pair txn = + std::pair const txn = transactionConstructImpl(preprocResult.second, ledger->rules(), app); if (!txn.second) @@ -963,13 +984,13 @@ transactionSubmit( { using namespace detail; - auto const& ledger = app.openLedger().current(); - auto j = app.journal("RPCHandler"); + auto const& ledger = app.getOpenLedger().current(); + auto j = app.getJournal("RPCHandler"); JLOG(j.debug()) << "transactionSubmit: " << jvRequest; // Add and amend fields based on the transaction type. SigningForParams signForParams; - transactionPreProcessResult preprocResult = + transactionPreProcessResult const preprocResult = transactionPreProcessImpl(jvRequest, role, signForParams, validatedLedgerAge, app); if (!preprocResult.second) @@ -1024,8 +1045,10 @@ checkMultiSignFields(Json::Value const& jvRequest) // Account. if (!jvRequest.isMember(jss::signature_target) && !tx_json[sfSigningPubKey.getJsonName()].asString().empty()) + { return RPC::make_error( rpcINVALID_PARAMS, "When multi-signing 'tx_json.SigningPubKey' must be empty."); + } return Json::Value(); } @@ -1083,8 +1106,8 @@ transactionSignFor( std::chrono::seconds validatedLedgerAge, Application& app) { - auto const& ledger = app.openLedger().current(); - auto j = app.journal("RPCHandler"); + auto const& ledger = app.getOpenLedger().current(); + auto j = app.getJournal("RPCHandler"); JLOG(j.debug()) << "transactionSignFor: " << jvRequest; // Verify presence of the signer's account field. @@ -1127,7 +1150,7 @@ transactionSignFor( // Add and amend fields based on the transaction type. SigningForParams signForParams(*signerAccountID); - transactionPreProcessResult preprocResult = + transactionPreProcessResult const preprocResult = transactionPreProcessImpl(jvRequest, role, signForParams, validatedLedgerAge, app); if (!preprocResult.second) @@ -1137,7 +1160,8 @@ transactionSignFor( signForParams.validMultiSign(), "xrpl::RPC::transactionSignFor : valid multi-signature"); { - std::shared_ptr account_state = ledger->read(keylet::account(*signerAccountID)); + std::shared_ptr const account_state = + ledger->read(keylet::account(*signerAccountID)); // Make sure the account and secret belong together. auto const err = acctMatchesPubKey(account_state, *signerAccountID, signForParams.getPublicKey()); @@ -1175,7 +1199,7 @@ transactionSignFor( } // Make sure the STTx makes a legitimate Transaction. - std::pair txn = + std::pair const txn = transactionConstructImpl(sttx, ledger->rules(), app); if (!txn.second) @@ -1195,8 +1219,8 @@ transactionSubmitMultiSigned( Application& app, ProcessTransactionFn const& processTransaction) { - auto const& ledger = app.openLedger().current(); - auto j = app.journal("RPCHandler"); + auto const& ledger = app.getOpenLedger().current(); + auto j = app.getJournal("RPCHandler"); JLOG(j.debug()) << "transactionSubmitMultiSigned: " << jvRequest; // When multi-signing, the "Sequence" and "SigningPubKey" fields must @@ -1222,7 +1246,7 @@ transactionSubmitMultiSigned( if (RPC::contains_error(txJsonResult)) return std::move(txJsonResult); - std::shared_ptr sle = ledger->read(keylet::account(srcAddressID)); + std::shared_ptr const sle = ledger->read(keylet::account(srcAddressID)); if (!sle) { @@ -1268,7 +1292,7 @@ transactionSubmitMultiSigned( } catch (std::exception& ex) { - std::string reason(ex.what()); + std::string const reason(ex.what()); return RPC::make_error( rpcINTERNAL, "Exception while serializing transaction: " + reason); } diff --git a/src/xrpld/app/paths/TrustLine.cpp b/src/xrpld/rpc/detail/TrustLine.cpp similarity index 96% rename from src/xrpld/app/paths/TrustLine.cpp rename to src/xrpld/rpc/detail/TrustLine.cpp index 6c54aa52d7..c2bc152448 100644 --- a/src/xrpld/app/paths/TrustLine.cpp +++ b/src/xrpld/rpc/detail/TrustLine.cpp @@ -1,5 +1,6 @@ -#include +#include +#include #include #include diff --git a/src/xrpld/app/paths/TrustLine.h b/src/xrpld/rpc/detail/TrustLine.h similarity index 100% rename from src/xrpld/app/paths/TrustLine.h rename to src/xrpld/rpc/detail/TrustLine.h diff --git a/src/xrpld/rpc/handlers/ChannelVerify.cpp b/src/xrpld/rpc/handlers/ChannelVerify.cpp new file mode 100644 index 0000000000..91b23db4e6 --- /dev/null +++ b/src/xrpld/rpc/handlers/ChannelVerify.cpp @@ -0,0 +1,71 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +// { +// public_key: +// channel_id: 256-bit channel id +// drops: 64-bit uint (as string) +// signature: signature to verify +// } +Json::Value +doChannelVerify(RPC::JsonContext& context) +{ + auto const& params(context.params); + for (auto const& p : {jss::public_key, jss::channel_id, jss::amount, jss::signature}) + { + if (!params.isMember(p)) + return RPC::missing_field_error(p); + } + + std::optional pk; + { + std::string const strPk = params[jss::public_key].asString(); + pk = parseBase58(TokenType::AccountPublic, strPk); + + if (!pk) + { + auto pkHex = strUnHex(strPk); + if (!pkHex) + return rpcError(rpcPUBLIC_MALFORMED); + auto const pkType = publicKeyType(makeSlice(*pkHex)); + if (!pkType) + return rpcError(rpcPUBLIC_MALFORMED); + pk.emplace(makeSlice(*pkHex)); + } + } + + uint256 channelId; + if (!channelId.parseHex(params[jss::channel_id].asString())) + return rpcError(rpcCHANNEL_MALFORMED); + + std::optional const optDrops = + params[jss::amount].isString() ? to_uint64(params[jss::amount].asString()) : std::nullopt; + + if (!optDrops) + return rpcError(rpcCHANNEL_AMT_MALFORMED); + + std::uint64_t const drops = *optDrops; + + auto sig = strUnHex(params[jss::signature].asString()); + if (!sig || sig->empty()) + return rpcError(rpcINVALID_PARAMS); + + Serializer msg; + serializePayChanAuthorization(msg, channelId, XRPAmount(drops)); + + Json::Value result; + result[jss::signature_verified] = verify(*pk, msg.slice(), makeSlice(*sig)); + return result; +} + +} // namespace xrpl diff --git a/src/xrpld/rpc/handlers/Handlers.h b/src/xrpld/rpc/handlers/Handlers.h index a5a96baa31..23328cf52a 100644 --- a/src/xrpld/rpc/handlers/Handlers.h +++ b/src/xrpld/rpc/handlers/Handlers.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { @@ -19,7 +19,7 @@ doAccountObjects(RPC::JsonContext&); Json::Value doAccountOffers(RPC::JsonContext&); Json::Value -doAccountTxJson(RPC::JsonContext&); +doAccountTx(RPC::JsonContext&); Json::Value doAMMInfo(RPC::JsonContext&); Json::Value diff --git a/src/xrpld/rpc/handlers/VaultInfo.cpp b/src/xrpld/rpc/handlers/VaultInfo.cpp index b17a16cc4a..4a704e0b0b 100644 --- a/src/xrpld/rpc/handlers/VaultInfo.cpp +++ b/src/xrpld/rpc/handlers/VaultInfo.cpp @@ -35,8 +35,7 @@ parseVault(Json::Value const& params, Json::Value& jvResult) RPC::inject_error(rpcACT_MALFORMED, jvResult); return std::nullopt; } - else if ( - !(params[jss::seq].isInt() || params[jss::seq].isUInt()) || + if (!(params[jss::seq].isInt() || params[jss::seq].isUInt()) || params[jss::seq].asDouble() <= 0.0 || params[jss::seq].asDouble() > double(Json::Value::maxUInt)) { diff --git a/src/xrpld/rpc/handlers/AccountChannels.cpp b/src/xrpld/rpc/handlers/account/AccountChannels.cpp similarity index 98% rename from src/xrpld/rpc/handlers/AccountChannels.cpp rename to src/xrpld/rpc/handlers/account/AccountChannels.cpp index 062a2b69a0..7bf8a03f21 100644 --- a/src/xrpld/rpc/handlers/AccountChannels.cpp +++ b/src/xrpld/rpc/handlers/account/AccountChannels.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -66,7 +67,7 @@ doAccountChannels(RPC::JsonContext& context) { return rpcError(rpcACT_MALFORMED); } - AccountID const accountID{std::move(id.value())}; + AccountID const accountID{id.value()}; if (!ledger->exists(keylet::account(accountID))) return rpcError(rpcACT_NOT_FOUND); @@ -81,7 +82,7 @@ doAccountChannels(RPC::JsonContext& context) if (!strDst.empty() && !raDstAccount) return rpcError(rpcACT_MALFORMED); - unsigned int limit; + unsigned int limit = 0; if (auto err = readLimitField(limit, RPC::Tuning::accountChannels, context)) return *err; diff --git a/src/xrpld/rpc/handlers/AccountCurrenciesHandler.cpp b/src/xrpld/rpc/handlers/account/AccountCurrencies.cpp similarity index 96% rename from src/xrpld/rpc/handlers/AccountCurrenciesHandler.cpp rename to src/xrpld/rpc/handlers/account/AccountCurrencies.cpp index 2ec1e20875..e509a72862 100644 --- a/src/xrpld/rpc/handlers/AccountCurrenciesHandler.cpp +++ b/src/xrpld/rpc/handlers/account/AccountCurrencies.cpp @@ -1,6 +1,6 @@ -#include #include #include +#include #include #include @@ -44,7 +44,7 @@ doAccountCurrencies(RPC::JsonContext& context) RPC::inject_error(rpcACT_MALFORMED, result); return result; } - auto const accountID{std::move(id.value())}; + auto const accountID{id.value()}; if (!ledger->exists(keylet::account(accountID))) return rpcError(rpcACT_NOT_FOUND); diff --git a/src/xrpld/rpc/handlers/AccountInfo.cpp b/src/xrpld/rpc/handlers/account/AccountInfo.cpp similarity index 98% rename from src/xrpld/rpc/handlers/AccountInfo.cpp rename to src/xrpld/rpc/handlers/account/AccountInfo.cpp index e88da097f7..becaea8a51 100644 --- a/src/xrpld/rpc/handlers/AccountInfo.cpp +++ b/src/xrpld/rpc/handlers/account/AccountInfo.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -84,7 +85,9 @@ doAccountInfo(RPC::JsonContext& context) strIdent = params[jss::ident].asString(); } else + { return RPC::missing_field_error(jss::account); + } std::shared_ptr ledger; auto result = RPC::lookupLedger(ledger, context); @@ -99,7 +102,7 @@ doAccountInfo(RPC::JsonContext& context) RPC::inject_error(rpcACT_MALFORMED, result); return result; } - auto const accountID{std::move(id.value())}; + auto const accountID{id.value()}; static constexpr std::array, 9> lsFlags{ {{"defaultRipple", lsfDefaultRipple}, @@ -150,12 +153,16 @@ doAccountInfo(RPC::JsonContext& context) acctFlags[lsf.first.data()] = sleAccepted->isFlag(lsf.second); if (ledger->rules().enabled(featureClawback)) + { acctFlags[allowTrustLineClawbackFlag.first.data()] = sleAccepted->isFlag(allowTrustLineClawbackFlag.second); + } if (ledger->rules().enabled(featureTokenEscrow)) + { acctFlags[allowTrustLineLockingFlag.first.data()] = sleAccepted->isFlag(allowTrustLineLockingFlag.second); + } result[jss::account_flags] = std::move(acctFlags); @@ -283,9 +290,9 @@ doAccountInfo(RPC::JsonContext& context) jvQueueTx.append(std::move(jvTx)); } - if (seqCount) + if (seqCount != 0u) jvQueueData[jss::sequence_count] = seqCount; - if (ticketCount) + if (ticketCount != 0u) jvQueueData[jss::ticket_count] = ticketCount; if (lowestSeq) jvQueueData[jss::lowest_sequence] = *lowestSeq; @@ -300,7 +307,9 @@ doAccountInfo(RPC::JsonContext& context) jvQueueData[jss::max_spend_drops_total] = to_string(totalSpend); } else + { jvQueueData[jss::txn_count] = 0u; + } result[jss::queue_data] = std::move(jvQueueData); } diff --git a/src/xrpld/rpc/handlers/AccountLines.cpp b/src/xrpld/rpc/handlers/account/AccountLines.cpp similarity index 96% rename from src/xrpld/rpc/handlers/AccountLines.cpp rename to src/xrpld/rpc/handlers/account/AccountLines.cpp index bb61e6f6c0..24ebfaa446 100644 --- a/src/xrpld/rpc/handlers/AccountLines.cpp +++ b/src/xrpld/rpc/handlers/account/AccountLines.cpp @@ -1,10 +1,11 @@ -#include #include #include #include +#include #include #include +#include #include #include #include @@ -80,7 +81,7 @@ doAccountLines(RPC::JsonContext& context) RPC::inject_error(rpcACT_MALFORMED, result); return result; } - auto const accountID{std::move(id.value())}; + auto const accountID{id.value()}; if (!ledger->exists(keylet::account(accountID))) return rpcError(rpcACT_NOT_FOUND); @@ -98,13 +99,13 @@ doAccountLines(RPC::JsonContext& context) return result; } - unsigned int limit; + unsigned int limit = 0; if (auto err = readLimitField(limit, RPC::Tuning::accountLines, context)) return *err; // this flag allows the requester to ask incoming trustlines in default // state be omitted - bool ignoreDefault = + bool const ignoreDefault = params.isMember(jss::ignore_default) && params[jss::ignore_default].asBool(); Json::Value& jsonLines(result[jss::lines] = Json::arrayValue); @@ -191,9 +192,13 @@ doAccountLines(RPC::JsonContext& context) if (visitData.ignoreDefault) { if (sleCur->getFieldAmount(sfLowLimit).getIssuer() == visitData.accountID) + { ignore = !(sleCur->getFieldU32(sfFlags) & lsfLowReserve); + } else + { ignore = !(sleCur->getFieldU32(sfFlags) & lsfHighReserve); + } } if (!ignore && count <= limit) diff --git a/src/xrpld/rpc/handlers/account/AccountNFTs.cpp b/src/xrpld/rpc/handlers/account/AccountNFTs.cpp new file mode 100644 index 0000000000..e1ead76e85 --- /dev/null +++ b/src/xrpld/rpc/handlers/account/AccountNFTs.cpp @@ -0,0 +1,160 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +/** General RPC command that can retrieve objects in the account root. + { + account: + ledger_hash: // optional + ledger_index: // optional + type: // optional, defaults to all account objects types + limit: // optional + marker: // optional, resume previous query + } +*/ +Json::Value +doAccountNFTs(RPC::JsonContext& context) +{ + auto const& params = context.params; + if (!params.isMember(jss::account)) + return RPC::missing_field_error(jss::account); + + if (!params[jss::account].isString()) + return RPC::invalid_field_error(jss::account); + + auto id = parseBase58(params[jss::account].asString()); + if (!id) + { + return rpcError(rpcACT_MALFORMED); + } + + std::shared_ptr ledger; + auto result = RPC::lookupLedger(ledger, context); + if (ledger == nullptr) + return result; + auto const accountID{id.value()}; + + if (!ledger->exists(keylet::account(accountID))) + return rpcError(rpcACT_NOT_FOUND); + + unsigned int limit = 0; + if (auto err = readLimitField(limit, RPC::Tuning::accountNFTokens, context)) + return *err; + + uint256 marker; + bool const markerSet = params.isMember(jss::marker); + + if (markerSet) + { + auto const& m = params[jss::marker]; + if (!m.isString()) + return RPC::expected_field_error(jss::marker, "string"); + + if (!marker.parseHex(m.asString())) + return RPC::invalid_field_error(jss::marker); + } + + auto const first = keylet::nftpage(keylet::nftpage_min(accountID), marker); + auto const last = keylet::nftpage_max(accountID); + + auto cp = ledger->read( + Keylet(ltNFTOKEN_PAGE, ledger->succ(first.key, last.key.next()).value_or(last.key))); + + std::uint32_t cnt = 0; + auto& nfts = (result[jss::account_nfts] = Json::arrayValue); + + // Continue iteration from the current page: + bool pastMarker = marker.isZero(); + bool markerFound = false; + uint256 const maskedMarker = marker & nft::pageMask; + while (cp) + { + auto arr = cp->getFieldArray(sfNFTokens); + + for (auto const& o : arr) + { + // Scrolling past the marker gets weird. We need to look at + // a couple of conditions. + // + // 1. If the low 96-bits don't match, then we compare only + // against the low 96-bits, since that's what determines + // the sort order of the pages. + // + // 2. However, within one page there can be a number of + // NFTokenIDs that all have the same low 96 bits. If we're + // in that case then we need to compare against the full + // 256 bits. + uint256 const nftokenID = o[sfNFTokenID]; + uint256 const maskedNftokenID = nftokenID & nft::pageMask; + + if (!pastMarker) + { + if (maskedNftokenID < maskedMarker) + continue; + + if (maskedNftokenID == maskedMarker && nftokenID < marker) + continue; + + if (nftokenID == marker) + { + markerFound = true; + continue; + } + } + + if (markerSet && !markerFound) + return RPC::invalid_field_error(jss::marker); + + pastMarker = true; + + { + Json::Value& obj = nfts.append(o.getJson(JsonOptions::none)); + + // Pull out the components of the nft ID. + obj[sfFlags.jsonName] = nft::getFlags(nftokenID); + obj[sfIssuer.jsonName] = to_string(nft::getIssuer(nftokenID)); + obj[sfNFTokenTaxon.jsonName] = nft::toUInt32(nft::getTaxon(nftokenID)); + obj[jss::nft_serial] = nft::getSerial(nftokenID); + if (std::uint16_t const xferFee = {nft::getTransferFee(nftokenID)}) + obj[sfTransferFee.jsonName] = xferFee; + } + + if (++cnt == limit) + { + result[jss::limit] = limit; + result[jss::marker] = to_string(o.getFieldH256(sfNFTokenID)); + return result; + } + } + + if (auto npm = (*cp)[~sfNextPageMin]) + { + cp = ledger->read(Keylet(ltNFTOKEN_PAGE, *npm)); + } + else + { + cp = nullptr; + } + } + + if (markerSet && !markerFound) + return RPC::invalid_field_error(jss::marker); + + result[jss::account] = toBase58(accountID); + context.loadType = Resource::feeMediumBurdenRPC; + return result; +} + +} // namespace xrpl diff --git a/src/xrpld/rpc/handlers/AccountObjects.cpp b/src/xrpld/rpc/handlers/account/AccountObjects.cpp similarity index 69% rename from src/xrpld/rpc/handlers/AccountObjects.cpp rename to src/xrpld/rpc/handlers/account/AccountObjects.cpp index e01822e901..2e8462de2d 100644 --- a/src/xrpld/rpc/handlers/AccountObjects.cpp +++ b/src/xrpld/rpc/handlers/account/AccountObjects.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -11,152 +12,11 @@ #include #include #include -#include #include namespace xrpl { -/** General RPC command that can retrieve objects in the account root. - { - account: - ledger_hash: // optional - ledger_index: // optional - type: // optional, defaults to all account objects types - limit: // optional - marker: // optional, resume previous query - } -*/ - -Json::Value -doAccountNFTs(RPC::JsonContext& context) -{ - auto const& params = context.params; - if (!params.isMember(jss::account)) - return RPC::missing_field_error(jss::account); - - if (!params[jss::account].isString()) - return RPC::invalid_field_error(jss::account); - - auto id = parseBase58(params[jss::account].asString()); - if (!id) - { - return rpcError(rpcACT_MALFORMED); - } - - std::shared_ptr ledger; - auto result = RPC::lookupLedger(ledger, context); - if (ledger == nullptr) - return result; - auto const accountID{id.value()}; - - if (!ledger->exists(keylet::account(accountID))) - return rpcError(rpcACT_NOT_FOUND); - - unsigned int limit; - if (auto err = readLimitField(limit, RPC::Tuning::accountNFTokens, context)) - return *err; - - uint256 marker; - bool const markerSet = params.isMember(jss::marker); - - if (markerSet) - { - auto const& m = params[jss::marker]; - if (!m.isString()) - return RPC::expected_field_error(jss::marker, "string"); - - if (!marker.parseHex(m.asString())) - return RPC::invalid_field_error(jss::marker); - } - - auto const first = keylet::nftpage(keylet::nftpage_min(accountID), marker); - auto const last = keylet::nftpage_max(accountID); - - auto cp = ledger->read( - Keylet(ltNFTOKEN_PAGE, ledger->succ(first.key, last.key.next()).value_or(last.key))); - - std::uint32_t cnt = 0; - auto& nfts = (result[jss::account_nfts] = Json::arrayValue); - - // Continue iteration from the current page: - bool pastMarker = marker.isZero(); - bool markerFound = false; - uint256 const maskedMarker = marker & nft::pageMask; - while (cp) - { - auto arr = cp->getFieldArray(sfNFTokens); - - for (auto const& o : arr) - { - // Scrolling past the marker gets weird. We need to look at - // a couple of conditions. - // - // 1. If the low 96-bits don't match, then we compare only - // against the low 96-bits, since that's what determines - // the sort order of the pages. - // - // 2. However, within one page there can be a number of - // NFTokenIDs that all have the same low 96 bits. If we're - // in that case then we need to compare against the full - // 256 bits. - uint256 const nftokenID = o[sfNFTokenID]; - uint256 const maskedNftokenID = nftokenID & nft::pageMask; - - if (!pastMarker) - { - if (maskedNftokenID < maskedMarker) - continue; - - if (maskedNftokenID == maskedMarker && nftokenID < marker) - continue; - - if (nftokenID == marker) - { - markerFound = true; - continue; - } - } - - if (markerSet && !markerFound) - return RPC::invalid_field_error(jss::marker); - - pastMarker = true; - - { - Json::Value& obj = nfts.append(o.getJson(JsonOptions::none)); - - // Pull out the components of the nft ID. - obj[sfFlags.jsonName] = nft::getFlags(nftokenID); - obj[sfIssuer.jsonName] = to_string(nft::getIssuer(nftokenID)); - obj[sfNFTokenTaxon.jsonName] = nft::toUInt32(nft::getTaxon(nftokenID)); - obj[jss::nft_serial] = nft::getSerial(nftokenID); - if (std::uint16_t xferFee = {nft::getTransferFee(nftokenID)}) - obj[sfTransferFee.jsonName] = xferFee; - } - - if (++cnt == limit) - { - result[jss::limit] = limit; - result[jss::marker] = to_string(o.getFieldH256(sfNFTokenID)); - return result; - } - } - - if (auto npm = (*cp)[~sfNextPageMin]) - cp = ledger->read(Keylet(ltNFTOKEN_PAGE, *npm)); - else - cp = nullptr; - } - - if (markerSet && !markerFound) - return RPC::invalid_field_error(jss::marker); - - result[jss::account] = toBase58(accountID); - context.loadType = Resource::feeMediumBurdenRPC; - return result; -} - /** Gathers all objects for an account in a ledger. @param ledger Ledger to search account objects. @param account AccountID to find objects for. @@ -229,9 +89,13 @@ getAccountObjects( jvObjects.append(cp->getJson(JsonOptions::none)); auto const npm = (*cp)[~sfNextPageMin]; if (npm) + { cp = ledger.read(Keylet(ltNFTOKEN_PAGE, *npm)); + } else + { cp = nullptr; + } if (--mlimit == 0) { @@ -429,13 +293,13 @@ doAccountObjects(RPC::JsonContext& context) rpcStatus.inject(result); return result; } - else if (type != ltANY) + if (type != ltANY) { typeFilter = std::vector({type}); } } - unsigned int limit; + unsigned int limit = 0; if (auto err = readLimitField(limit, RPC::Tuning::accountObjects, context)) return *err; diff --git a/src/xrpld/rpc/handlers/AccountOffers.cpp b/src/xrpld/rpc/handlers/account/AccountOffers.cpp similarity index 95% rename from src/xrpld/rpc/handlers/AccountOffers.cpp rename to src/xrpld/rpc/handlers/account/AccountOffers.cpp index 130decad04..38cc7c1dc5 100644 --- a/src/xrpld/rpc/handlers/AccountOffers.cpp +++ b/src/xrpld/rpc/handlers/account/AccountOffers.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -16,7 +17,7 @@ namespace xrpl { void appendOfferJson(std::shared_ptr const& offer, Json::Value& offers) { - STAmount dirRate = amountFromQuality(getQuality(offer->getFieldH256(sfBookDirectory))); + STAmount const dirRate = amountFromQuality(getQuality(offer->getFieldH256(sfBookDirectory))); Json::Value& obj(offers.append(Json::objectValue)); offer->getFieldAmount(sfTakerPays).setJson(obj[jss::taker_pays]); offer->getFieldAmount(sfTakerGets).setJson(obj[jss::taker_gets]); @@ -55,7 +56,7 @@ doAccountOffers(RPC::JsonContext& context) RPC::inject_error(rpcACT_MALFORMED, result); return result; } - auto const accountID{std::move(id.value())}; + auto const accountID{id.value()}; // Get info on account. result[jss::account] = toBase58(accountID); @@ -63,7 +64,7 @@ doAccountOffers(RPC::JsonContext& context) if (!ledger->exists(keylet::account(accountID))) return rpcError(rpcACT_NOT_FOUND); - unsigned int limit; + unsigned int limit = 0; if (auto err = readLimitField(limit, RPC::Tuning::accountOffers, context)) return *err; diff --git a/src/xrpld/rpc/handlers/AccountTx.cpp b/src/xrpld/rpc/handlers/account/AccountTx.cpp similarity index 91% rename from src/xrpld/rpc/handlers/AccountTx.cpp rename to src/xrpld/rpc/handlers/account/AccountTx.cpp index a6dcbb7b9d..acd5912ee0 100644 --- a/src/xrpld/rpc/handlers/AccountTx.cpp +++ b/src/xrpld/rpc/handlers/account/AccountTx.cpp @@ -41,30 +41,30 @@ parseLedgerArgs(RPC::Context& context, Json::Value const& params) if ((params.isMember(jss::ledger_index_min) || params.isMember(jss::ledger_index_max)) && (params.isMember(jss::ledger_hash) || params.isMember(jss::ledger_index))) { - RPC::Status status{rpcINVALID_PARAMS, "invalidParams"}; + RPC::Status const status{rpcINVALID_PARAMS, "invalidParams"}; status.inject(response); return response; } } if (params.isMember(jss::ledger_index_min) || params.isMember(jss::ledger_index_max)) { - uint32_t min = + uint32_t const min = params.isMember(jss::ledger_index_min) && params[jss::ledger_index_min].asInt() >= 0 ? params[jss::ledger_index_min].asUInt() : 0; - uint32_t max = + uint32_t const max = params.isMember(jss::ledger_index_max) && params[jss::ledger_index_max].asInt() >= 0 ? params[jss::ledger_index_max].asUInt() : UINT32_MAX; return LedgerRange{min, max}; } - else if (params.isMember(jss::ledger_hash)) + if (params.isMember(jss::ledger_hash)) { auto& hashValue = params[jss::ledger_hash]; if (!hashValue.isString()) { - RPC::Status status{rpcINVALID_PARAMS, "ledgerHashNotString"}; + RPC::Status const status{rpcINVALID_PARAMS, "ledgerHashNotString"}; status.inject(response); return response; } @@ -72,30 +72,38 @@ parseLedgerArgs(RPC::Context& context, Json::Value const& params) LedgerHash hash; if (!hash.parseHex(hashValue.asString())) { - RPC::Status status{rpcINVALID_PARAMS, "ledgerHashMalformed"}; + RPC::Status const status{rpcINVALID_PARAMS, "ledgerHashMalformed"}; status.inject(response); return response; } return hash; } - else if (params.isMember(jss::ledger_index)) + if (params.isMember(jss::ledger_index)) { LedgerSpecifier ledger; if (params[jss::ledger_index].isNumeric()) + { ledger = params[jss::ledger_index].asUInt(); + } else { - std::string ledgerStr = params[jss::ledger_index].asString(); + std::string const ledgerStr = params[jss::ledger_index].asString(); if (ledgerStr == "current" || ledgerStr.empty()) + { ledger = LedgerShortcut::Current; + } else if (ledgerStr == "closed") + { ledger = LedgerShortcut::Closed; + } else if (ledgerStr == "validated") + { ledger = LedgerShortcut::Validated; + } else { - RPC::Status status{rpcINVALID_PARAMS, "ledger_index string malformed"}; + RPC::Status const status{rpcINVALID_PARAMS, "ledger_index string malformed"}; status.inject(response); return response; } @@ -108,9 +116,9 @@ parseLedgerArgs(RPC::Context& context, Json::Value const& params) std::variant getLedgerRange(RPC::Context& context, std::optional const& ledgerSpecifier) { - std::uint32_t uValidatedMin; - std::uint32_t uValidatedMax; - bool bValidated = context.ledgerMaster.getValidatedRange(uValidatedMin, uValidatedMax); + std::uint32_t uValidatedMin = 0; + std::uint32_t uValidatedMax = 0; + bool const bValidated = context.ledgerMaster.getValidatedRange(uValidatedMin, uValidatedMax); if (!bValidated) { @@ -125,7 +133,7 @@ getLedgerRange(RPC::Context& context, std::optional const& ledg // Does request specify a ledger or ledger range? if (ledgerSpecifier) { - auto const status = std::visit( + auto status = std::visit( [&](auto const& ls) -> RPC::Status { using T = std::decay_t; if constexpr (std::is_same_v) @@ -159,13 +167,13 @@ getLedgerRange(RPC::Context& context, std::optional const& ledg else { std::shared_ptr ledgerView; - auto const status = getLedger(ledgerView, ls, context); + auto status = getLedger(ledgerView, ls, context); if (!ledgerView) { return status; } - bool validated = context.ledgerMaster.isValidated(*ledgerView); + bool const validated = context.ledgerMaster.isValidated(*ledgerView); if (!validated || ledgerView->header().seq > uValidatedMax || ledgerView->header().seq < uValidatedMin) @@ -202,13 +210,8 @@ doAccountTxHelp(RPC::Context& context, AccountTxArgs const& args) result.marker = args.marker; - RelationalDatabase::AccountTxPageOptions options = { - args.account, - result.ledgerRange.min, - result.ledgerRange.max, - result.marker, - args.limit, - isUnlimited(context.role)}; + RelationalDatabase::AccountTxPageOptions const options = { + args.account, result.ledgerRange, result.marker, args.limit, isUnlimited(context.role)}; auto& db = context.app.getRelationalDatabase(); @@ -298,7 +301,9 @@ populateJsonResponse( jvObj[jss::close_time_iso] = to_string_iso(*closeTime); } else + { jvObj[json_tx] = txn->getJson(JsonOptions::include_date); + } auto const& sttx = txn->getSTransaction(); RPC::insertDeliverMax(jvObj[json_tx], sttx->getTxnType(), context.apiVersion); @@ -359,7 +364,7 @@ populateJsonResponse( // resume previous query // } Json::Value -doAccountTxJson(RPC::JsonContext& context) +doAccountTx(RPC::JsonContext& context) { if (!context.app.config().useTxTables()) return rpcError(rpcNOT_ENABLED); @@ -404,10 +409,8 @@ doAccountTxJson(RPC::JsonContext& context) { return *jv; } - else - { - args.ledger = std::get>(parseRes); - } + + args.ledger = std::get>(parseRes); if (params.isMember(jss::marker)) { @@ -416,7 +419,7 @@ doAccountTxJson(RPC::JsonContext& context) !token[jss::ledger].isConvertibleTo(Json::ValueType::uintValue) || !token[jss::seq].isConvertibleTo(Json::ValueType::uintValue)) { - RPC::Status status{ + RPC::Status const status{ rpcINVALID_PARAMS, "invalid marker. Provide ledger index via ledger field, and " "transaction sequence number via seq field"}; diff --git a/src/xrpld/rpc/handlers/GatewayBalances.cpp b/src/xrpld/rpc/handlers/account/GatewayBalances.cpp similarity index 96% rename from src/xrpld/rpc/handlers/GatewayBalances.cpp rename to src/xrpld/rpc/handlers/account/GatewayBalances.cpp index a2176ab388..20679e5f42 100644 --- a/src/xrpld/rpc/handlers/GatewayBalances.cpp +++ b/src/xrpld/rpc/handlers/account/GatewayBalances.cpp @@ -1,9 +1,10 @@ #include -#include #include #include +#include #include +#include #include #include #include @@ -54,7 +55,7 @@ doGatewayBalances(RPC::JsonContext& context) auto id = parseBase58(strIdent); if (!id) return rpcError(rpcACT_MALFORMED); - auto const accountID{std::move(id.value())}; + auto const accountID{id.value()}; context.loadType = Resource::feeHeavyBurdenRPC; result[jss::account] = toBase58(accountID); @@ -75,7 +76,7 @@ doGatewayBalances(RPC::JsonContext& context) { if (auto id = parseBase58(j.asString()); id) { - hotWallets.insert(std::move(id.value())); + hotWallets.insert(id.value()); return true; } } @@ -163,7 +164,7 @@ doGatewayBalances(RPC::JsonContext& context) if (!rs) return; - int balSign = rs->getBalance().signum(); + int const balSign = rs->getBalance().signum(); if (balSign == 0) return; @@ -173,7 +174,7 @@ doGatewayBalances(RPC::JsonContext& context) // A positive balance means the cold wallet has an asset // (unusual) - if (hotWallets.count(peer) > 0) + if (hotWallets.contains(peer)) { // This is a specified hot wallet hotBalances[peer].push_back(-rs->getBalance()); diff --git a/src/xrpld/rpc/handlers/NoRippleCheck.cpp b/src/xrpld/rpc/handlers/account/NoRippleCheck.cpp similarity index 89% rename from src/xrpld/rpc/handlers/NoRippleCheck.cpp rename to src/xrpld/rpc/handlers/account/NoRippleCheck.cpp index e17a437efc..d00a3e279b 100644 --- a/src/xrpld/rpc/handlers/NoRippleCheck.cpp +++ b/src/xrpld/rpc/handlers/account/NoRippleCheck.cpp @@ -1,11 +1,12 @@ #include -#include #include #include #include +#include #include #include +#include #include #include #include @@ -55,12 +56,16 @@ doNoRippleCheck(RPC::JsonContext& context) { std::string const role = params["role"].asString(); if (role == "gateway") + { roleGateway = true; + } else if (role != "user") + { return RPC::invalid_field_error("role"); + } } - unsigned int limit; + unsigned int limit = 0; if (auto err = readLimitField(limit, RPC::Tuning::noRippleCheck, context)) return *err; @@ -83,7 +88,7 @@ doNoRippleCheck(RPC::JsonContext& context) if (!ledger) return result; - Json::Value dummy; + Json::Value dummy; // NOLINT(misc-const-correctness) Json::Value& jvTransactions = transactions ? (result[jss::transactions] = Json::arrayValue) : dummy; @@ -93,7 +98,7 @@ doNoRippleCheck(RPC::JsonContext& context) RPC::inject_error(rpcACT_MALFORMED, result); return result; } - auto const accountID{std::move(id.value())}; + auto const accountID{id.value()}; auto const sle = ledger->read(keylet::account(accountID)); if (!sle) return rpcError(rpcACT_NOT_FOUND); @@ -102,16 +107,16 @@ doNoRippleCheck(RPC::JsonContext& context) Json::Value& problems = (result["problems"] = Json::arrayValue); - bool bDefaultRipple = sle->getFieldU32(sfFlags) & lsfDefaultRipple; + bool const bDefaultRipple = (sle->getFieldU32(sfFlags) & lsfDefaultRipple) != 0u; - if (bDefaultRipple & !roleGateway) + if ((static_cast(bDefaultRipple) & static_cast(!roleGateway)) != 0) { problems.append( "You appear to have set your default ripple flag even though you " "are not a gateway. This is not recommended unless you are " "experimenting"); } - else if (roleGateway & !bDefaultRipple) + else if ((static_cast(roleGateway) & static_cast(!bDefaultRipple)) != 0) { problems.append("You should immediately set your default ripple flag"); if (transactions) @@ -146,9 +151,10 @@ doNoRippleCheck(RPC::JsonContext& context) } if (needFix) { - AccountID peer = + AccountID const peer = ownedItem->getFieldAmount(bLow ? sfHighLimit : sfLowLimit).getIssuer(); - STAmount peerLimit = ownedItem->getFieldAmount(bLow ? sfHighLimit : sfLowLimit); + STAmount const peerLimit = + ownedItem->getFieldAmount(bLow ? sfHighLimit : sfLowLimit); problem += to_string(peerLimit.getCurrency()); problem += " line to "; problem += to_string(peerLimit.getIssuer()); diff --git a/src/xrpld/rpc/handlers/OwnerInfo.cpp b/src/xrpld/rpc/handlers/account/OwnerInfo.cpp similarity index 94% rename from src/xrpld/rpc/handlers/OwnerInfo.cpp rename to src/xrpld/rpc/handlers/account/OwnerInfo.cpp index 202d0e9b5b..659a149e20 100644 --- a/src/xrpld/rpc/handlers/OwnerInfo.cpp +++ b/src/xrpld/rpc/handlers/account/OwnerInfo.cpp @@ -20,7 +20,7 @@ doOwnerInfo(RPC::JsonContext& context) return RPC::missing_field_error(jss::account); } - std::string strIdent = context.params.isMember(jss::account) + std::string const strIdent = context.params.isMember(jss::account) ? context.params[jss::account].asString() : context.params[jss::ident].asString(); Json::Value ret; diff --git a/src/xrpld/rpc/handlers/BlackList.cpp b/src/xrpld/rpc/handlers/admin/BlackList.cpp similarity index 91% rename from src/xrpld/rpc/handlers/BlackList.cpp rename to src/xrpld/rpc/handlers/admin/BlackList.cpp index 102041b8d0..86abe53686 100644 --- a/src/xrpld/rpc/handlers/BlackList.cpp +++ b/src/xrpld/rpc/handlers/admin/BlackList.cpp @@ -11,9 +11,11 @@ doBlackList(RPC::JsonContext& context) { auto& rm = context.app.getResourceManager(); if (context.params.isMember(jss::threshold)) + { return rm.getJson(context.params[jss::threshold].asInt()); - else - return rm.getJson(); + } + + return rm.getJson(); } } // namespace xrpl diff --git a/src/xrpld/rpc/handlers/UnlList.cpp b/src/xrpld/rpc/handlers/admin/UnlList.cpp similarity index 92% rename from src/xrpld/rpc/handlers/UnlList.cpp rename to src/xrpld/rpc/handlers/admin/UnlList.cpp index 20fb692fbb..31f41b4a33 100644 --- a/src/xrpld/rpc/handlers/UnlList.cpp +++ b/src/xrpld/rpc/handlers/admin/UnlList.cpp @@ -12,7 +12,7 @@ doUnlList(RPC::JsonContext& context) { Json::Value obj(Json::objectValue); - context.app.validators().for_each_listed( + context.app.getValidators().for_each_listed( [&unl = obj[jss::unl]](PublicKey const& publicKey, bool trusted) { Json::Value node(Json::objectValue); diff --git a/src/xrpld/rpc/handlers/CanDelete.cpp b/src/xrpld/rpc/handlers/admin/data/CanDelete.cpp similarity index 94% rename from src/xrpld/rpc/handlers/CanDelete.cpp rename to src/xrpld/rpc/handlers/admin/data/CanDelete.cpp index 91466c2583..7d881e7d2e 100644 --- a/src/xrpld/rpc/handlers/CanDelete.cpp +++ b/src/xrpld/rpc/handlers/admin/data/CanDelete.cpp @@ -22,7 +22,7 @@ doCanDelete(RPC::JsonContext& context) if (context.params.isMember(jss::can_delete)) { - Json::Value canDelete = context.params.get(jss::can_delete, 0); + Json::Value const canDelete = context.params.get(jss::can_delete, 0); std::uint32_t canDeleteSeq = 0; if (canDelete.isUInt()) @@ -49,7 +49,7 @@ doCanDelete(RPC::JsonContext& context) else if (canDeleteStr == "now") { canDeleteSeq = context.app.getSHAMapStore().getLastRotated(); - if (!canDeleteSeq) + if (canDeleteSeq == 0u) return RPC::make_error(rpcNOT_READY); } else if (uint256 lh; lh.parseHex(canDeleteStr)) diff --git a/src/xrpld/rpc/handlers/LedgerCleanerHandler.cpp b/src/xrpld/rpc/handlers/admin/data/LedgerCleaner.cpp similarity index 100% rename from src/xrpld/rpc/handlers/LedgerCleanerHandler.cpp rename to src/xrpld/rpc/handlers/admin/data/LedgerCleaner.cpp diff --git a/src/xrpld/rpc/handlers/LedgerRequest.cpp b/src/xrpld/rpc/handlers/admin/data/LedgerRequest.cpp similarity index 100% rename from src/xrpld/rpc/handlers/LedgerRequest.cpp rename to src/xrpld/rpc/handlers/admin/data/LedgerRequest.cpp diff --git a/src/xrpld/rpc/handlers/ValidationCreate.cpp b/src/xrpld/rpc/handlers/admin/keygen/ValidationCreate.cpp similarity index 100% rename from src/xrpld/rpc/handlers/ValidationCreate.cpp rename to src/xrpld/rpc/handlers/admin/keygen/ValidationCreate.cpp diff --git a/src/xrpld/rpc/handlers/WalletPropose.cpp b/src/xrpld/rpc/handlers/admin/keygen/WalletPropose.cpp similarity index 90% rename from src/xrpld/rpc/handlers/WalletPropose.cpp rename to src/xrpld/rpc/handlers/admin/keygen/WalletPropose.cpp index 273d829670..428dfb5380 100644 --- a/src/xrpld/rpc/handlers/WalletPropose.cpp +++ b/src/xrpld/rpc/handlers/admin/keygen/WalletPropose.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include @@ -56,7 +56,7 @@ walletPropose(Json::Value const& params) { std::optional keyType; std::optional seed; - bool rippleLibSeed = false; + bool libSeed = false; if (params.isMember(jss::key_type)) { @@ -71,18 +71,22 @@ walletPropose(Json::Value const& params) return rpcError(rpcINVALID_PARAMS); } - // ripple-lib encodes seed used to generate an Ed25519 wallet in a + // XrplLib encodes seed used to generate an Ed25519 wallet in a // non-standard way. While we never encode seeds that way, we try // to detect such keys to avoid user confusion. { if (params.isMember(jss::passphrase)) - seed = RPC::parseRippleLibSeed(params[jss::passphrase]); + { + seed = RPC::parseXrplLibSeed(params[jss::passphrase]); + } else if (params.isMember(jss::seed)) - seed = RPC::parseRippleLibSeed(params[jss::seed]); + { + seed = RPC::parseXrplLibSeed(params[jss::seed]); + } if (seed) { - rippleLibSeed = true; + libSeed = true; // If the user *explicitly* requests a key type other than // Ed25519 we return an error. @@ -133,7 +137,7 @@ walletPropose(Json::Value const& params) // If a passphrase was specified, and it was hashed and used as a seed // run a quick entropy check and add an appropriate warning, because // "brain wallets" can be easily attacked. - if (!rippleLibSeed && params.isMember(jss::passphrase)) + if (!libSeed && params.isMember(jss::passphrase)) { auto const passphrase = params[jss::passphrase].asString(); @@ -142,15 +146,19 @@ walletPropose(Json::Value const& params) // 80 bits of entropy isn't bad, but it's better to // err on the side of caution and be conservative. if (estimate_entropy(passphrase) < 80.0) + { obj[jss::warning] = "This wallet was generated using a user-supplied " "passphrase that has low entropy and is vulnerable " "to brute-force attacks."; + } else + { obj[jss::warning] = "This wallet was generated using a user-supplied " "passphrase. It may be vulnerable to brute-force " "attacks."; + } } } diff --git a/src/xrpld/rpc/handlers/WalletPropose.h b/src/xrpld/rpc/handlers/admin/keygen/WalletPropose.h similarity index 100% rename from src/xrpld/rpc/handlers/WalletPropose.h rename to src/xrpld/rpc/handlers/admin/keygen/WalletPropose.h diff --git a/src/xrpld/rpc/handlers/LogLevel.cpp b/src/xrpld/rpc/handlers/admin/log/LogLevel.cpp similarity index 75% rename from src/xrpld/rpc/handlers/LogLevel.cpp rename to src/xrpld/rpc/handlers/admin/log/LogLevel.cpp index 2596cd6c70..e1e637435c 100644 --- a/src/xrpld/rpc/handlers/LogLevel.cpp +++ b/src/xrpld/rpc/handlers/admin/log/LogLevel.cpp @@ -21,9 +21,9 @@ doLogLevel(RPC::JsonContext& context) Json::Value ret(Json::objectValue); Json::Value lev(Json::objectValue); - lev[jss::base] = Logs::toString(Logs::fromSeverity(context.app.logs().threshold())); - std::vector> logTable( - context.app.logs().partition_severities()); + lev[jss::base] = Logs::toString(Logs::fromSeverity(context.app.getLogs().threshold())); + std::vector> const logTable( + context.app.getLogs().partition_severities()); for (auto const& [k, v] : logTable) lev[k] = v; @@ -41,7 +41,7 @@ doLogLevel(RPC::JsonContext& context) if (!context.params.isMember(jss::partition)) { // set base log threshold - context.app.logs().threshold(severity); + context.app.getLogs().threshold(severity); return Json::objectValue; } @@ -49,12 +49,16 @@ doLogLevel(RPC::JsonContext& context) if (context.params.isMember(jss::partition)) { // set partition threshold - std::string partition(context.params[jss::partition].asString()); + std::string const partition(context.params[jss::partition].asString()); if (boost::iequals(partition, "base")) - context.app.logs().threshold(severity); + { + context.app.getLogs().threshold(severity); + } else - context.app.logs().get(partition).threshold(severity); + { + context.app.getLogs().get(partition).threshold(severity); + } return Json::objectValue; } diff --git a/src/xrpld/rpc/handlers/LogRotate.cpp b/src/xrpld/rpc/handlers/admin/log/LogRotate.cpp similarity index 80% rename from src/xrpld/rpc/handlers/LogRotate.cpp rename to src/xrpld/rpc/handlers/admin/log/LogRotate.cpp index 3d52dc6538..3cc7f35381 100644 --- a/src/xrpld/rpc/handlers/LogRotate.cpp +++ b/src/xrpld/rpc/handlers/admin/log/LogRotate.cpp @@ -10,7 +10,7 @@ Json::Value doLogRotate(RPC::JsonContext& context) { context.app.getPerfLog().rotate(); - return RPC::makeObjectValue(context.app.logs().rotate()); + return RPC::makeObjectValue(context.app.getLogs().rotate()); } } // namespace xrpl diff --git a/src/xrpld/rpc/handlers/Connect.cpp b/src/xrpld/rpc/handlers/admin/peer/Connect.cpp similarity index 92% rename from src/xrpld/rpc/handlers/Connect.cpp rename to src/xrpld/rpc/handlers/admin/peer/Connect.cpp index c76ebed546..fe3183484e 100644 --- a/src/xrpld/rpc/handlers/Connect.cpp +++ b/src/xrpld/rpc/handlers/admin/peer/Connect.cpp @@ -33,18 +33,22 @@ doConnect(RPC::JsonContext& context) return rpcError(rpcINVALID_PARAMS); } - int iPort; + int iPort = 0; if (context.params.isMember(jss::port)) + { iPort = context.params[jss::port].asInt(); + } else + { iPort = DEFAULT_PEER_PORT; + } auto const ip_str = context.params[jss::ip].asString(); auto ip = beast::IP::Endpoint::from_string(ip_str); if (!is_unspecified(ip)) - context.app.overlay().connect(ip.at_port(iPort)); + context.app.getOverlay().connect(ip.at_port(iPort)); return RPC::makeObjectValue( "attempting connection to IP:" + ip_str + " port: " + std::to_string(iPort)); diff --git a/src/xrpld/rpc/handlers/Reservations.cpp b/src/xrpld/rpc/handlers/admin/peer/PeerReservationsAdd.cpp similarity index 61% rename from src/xrpld/rpc/handlers/Reservations.cpp rename to src/xrpld/rpc/handlers/admin/peer/PeerReservationsAdd.cpp index 5d9c23d46e..dcd97bb2e4 100644 --- a/src/xrpld/rpc/handlers/Reservations.cpp +++ b/src/xrpld/rpc/handlers/admin/peer/PeerReservationsAdd.cpp @@ -9,7 +9,6 @@ #include #include -#include namespace xrpl { @@ -56,7 +55,7 @@ doPeerReservationsAdd(RPC::JsonContext& context) PublicKey const& nodeId = *optPk; auto const previous = - context.app.peerReservations().insert_or_assign(PeerReservation{nodeId, desc}); + context.app.getPeerReservations().insert_or_assign(PeerReservation{nodeId, desc}); Json::Value result{Json::objectValue}; if (previous) @@ -66,46 +65,4 @@ doPeerReservationsAdd(RPC::JsonContext& context) return result; } -Json::Value -doPeerReservationsDel(RPC::JsonContext& context) -{ - auto const& params = context.params; - - // We repeat much of the parameter parsing from `doPeerReservationsAdd`. - if (!params.isMember(jss::public_key)) - return RPC::missing_field_error(jss::public_key); - if (!params[jss::public_key].isString()) - return RPC::expected_field_error(jss::public_key, "a string"); - - std::optional optPk = - parseBase58(TokenType::NodePublic, params[jss::public_key].asString()); - if (!optPk) - return rpcError(rpcPUBLIC_MALFORMED); - PublicKey const& nodeId = *optPk; - - auto const previous = context.app.peerReservations().erase(nodeId); - - Json::Value result{Json::objectValue}; - if (previous) - { - result[jss::previous] = previous->toJson(); - } - return result; -} - -Json::Value -doPeerReservationsList(RPC::JsonContext& context) -{ - auto const& reservations = context.app.peerReservations().list(); - // Enumerate the reservations in context.app.peerReservations() - // as a Json::Value. - Json::Value result{Json::objectValue}; - Json::Value& jaReservations = result[jss::reservations] = Json::arrayValue; - for (auto const& reservation : reservations) - { - jaReservations.append(reservation.toJson()); - } - return result; -} - } // namespace xrpl diff --git a/src/xrpld/rpc/handlers/admin/peer/PeerReservationsDel.cpp b/src/xrpld/rpc/handlers/admin/peer/PeerReservationsDel.cpp new file mode 100644 index 0000000000..14d017779d --- /dev/null +++ b/src/xrpld/rpc/handlers/admin/peer/PeerReservationsDel.cpp @@ -0,0 +1,41 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +Json::Value +doPeerReservationsDel(RPC::JsonContext& context) +{ + auto const& params = context.params; + + // We repeat much of the parameter parsing from `doPeerReservationsAdd`. + if (!params.isMember(jss::public_key)) + return RPC::missing_field_error(jss::public_key); + if (!params[jss::public_key].isString()) + return RPC::expected_field_error(jss::public_key, "a string"); + + std::optional optPk = + parseBase58(TokenType::NodePublic, params[jss::public_key].asString()); + if (!optPk) + return rpcError(rpcPUBLIC_MALFORMED); + PublicKey const& nodeId = *optPk; + + auto const previous = context.app.getPeerReservations().erase(nodeId); + + Json::Value result{Json::objectValue}; + if (previous) + { + result[jss::previous] = previous->toJson(); + } + return result; +} + +} // namespace xrpl diff --git a/src/xrpld/rpc/handlers/admin/peer/PeerReservationsList.cpp b/src/xrpld/rpc/handlers/admin/peer/PeerReservationsList.cpp new file mode 100644 index 0000000000..119af3f73c --- /dev/null +++ b/src/xrpld/rpc/handlers/admin/peer/PeerReservationsList.cpp @@ -0,0 +1,24 @@ +#include +#include + +#include +#include + +namespace xrpl { + +Json::Value +doPeerReservationsList(RPC::JsonContext& context) +{ + auto const& reservations = context.app.getPeerReservations().list(); + // Enumerate the reservations in context.app.getPeerReservations() + // as a Json::Value. + Json::Value result{Json::objectValue}; + Json::Value& jaReservations = result[jss::reservations] = Json::arrayValue; + for (auto const& reservation : reservations) + { + jaReservations.append(reservation.toJson()); + } + return result; +} + +} // namespace xrpl diff --git a/src/xrpld/rpc/handlers/Peers.cpp b/src/xrpld/rpc/handlers/admin/peer/Peers.cpp similarity index 80% rename from src/xrpld/rpc/handlers/Peers.cpp rename to src/xrpld/rpc/handlers/admin/peer/Peers.cpp index 1be136a5f8..646aae7bc8 100644 --- a/src/xrpld/rpc/handlers/Peers.cpp +++ b/src/xrpld/rpc/handlers/admin/peer/Peers.cpp @@ -15,7 +15,7 @@ doPeers(RPC::JsonContext& context) { Json::Value jvResult(Json::objectValue); - jvResult[jss::peers] = context.app.overlay().json(); + jvResult[jss::peers] = context.app.getOverlay().json(); // Legacy support if (context.apiVersion == 1) @@ -27,20 +27,24 @@ doPeers(RPC::JsonContext& context) auto const s = p[jss::track].asString(); if (s == "diverged") + { p["sanity"] = "insane"; + } else if (s == "unknown") + { p["sanity"] = "unknown"; + } } } } - auto const now = context.app.timeKeeper().now(); + auto const now = context.app.getTimeKeeper().now(); auto const self = context.app.nodeIdentity().first; Json::Value& cluster = (jvResult[jss::cluster] = Json::objectValue); - std::uint32_t ref = context.app.getFeeTrack().getLoadBase(); + std::uint32_t const ref = context.app.getFeeTrack().getLoadBase(); - context.app.cluster().for_each([&cluster, now, ref, &self](ClusterNode const& node) { + context.app.getCluster().for_each([&cluster, now, ref, &self](ClusterNode const& node) { if (node.identity() == self) return; @@ -53,8 +57,10 @@ doPeers(RPC::JsonContext& context) json[jss::fee] = static_cast(node.getLoadFee()) / ref; if (node.getReportTime() != NetClock::time_point{}) + { json[jss::age] = (node.getReportTime() >= now) ? 0 : (now - node.getReportTime()).count(); + } }); return jvResult; diff --git a/src/xrpld/rpc/handlers/LedgerAccept.cpp b/src/xrpld/rpc/handlers/admin/server_control/LedgerAccept.cpp similarity index 90% rename from src/xrpld/rpc/handlers/LedgerAccept.cpp rename to src/xrpld/rpc/handlers/admin/server_control/LedgerAccept.cpp index c7a828863a..91e88b707f 100644 --- a/src/xrpld/rpc/handlers/LedgerAccept.cpp +++ b/src/xrpld/rpc/handlers/admin/server_control/LedgerAccept.cpp @@ -22,7 +22,7 @@ doLedgerAccept(RPC::JsonContext& context) } else { - std::unique_lock lock{context.app.getMasterMutex()}; + std::unique_lock const lock{context.app.getMasterMutex()}; context.netOps.acceptLedger(); jvResult[jss::ledger_current_index] = context.ledgerMaster.getCurrentLedgerIndex(); } diff --git a/src/xrpld/rpc/handlers/Stop.cpp b/src/xrpld/rpc/handlers/admin/server_control/Stop.cpp similarity index 94% rename from src/xrpld/rpc/handlers/Stop.cpp rename to src/xrpld/rpc/handlers/admin/server_control/Stop.cpp index d2f1cd7a80..b47c35e21d 100644 --- a/src/xrpld/rpc/handlers/Stop.cpp +++ b/src/xrpld/rpc/handlers/admin/server_control/Stop.cpp @@ -7,7 +7,7 @@ namespace xrpl { namespace RPC { struct JsonContext; -} +} // namespace RPC Json::Value doStop(RPC::JsonContext& context) diff --git a/src/xrpld/rpc/handlers/PayChanClaim.cpp b/src/xrpld/rpc/handlers/admin/signing/ChannelAuthorize.cpp similarity index 59% rename from src/xrpld/rpc/handlers/PayChanClaim.cpp rename to src/xrpld/rpc/handlers/admin/signing/ChannelAuthorize.cpp index 44312ee336..73d185be44 100644 --- a/src/xrpld/rpc/handlers/PayChanClaim.cpp +++ b/src/xrpld/rpc/handlers/admin/signing/ChannelAuthorize.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -29,8 +28,10 @@ doChannelAuthorize(RPC::JsonContext& context) auto const& params(context.params); for (auto const& p : {jss::channel_id, jss::amount}) + { if (!params.isMember(p)) return RPC::missing_field_error(p); + } // Compatibility if a key type isn't specified. If it is, the // keypairForSignature code will validate parameters and return @@ -81,59 +82,4 @@ doChannelAuthorize(RPC::JsonContext& context) return result; } -// { -// public_key: -// channel_id: 256-bit channel id -// drops: 64-bit uint (as string) -// signature: signature to verify -// } -Json::Value -doChannelVerify(RPC::JsonContext& context) -{ - auto const& params(context.params); - for (auto const& p : {jss::public_key, jss::channel_id, jss::amount, jss::signature}) - if (!params.isMember(p)) - return RPC::missing_field_error(p); - - std::optional pk; - { - std::string const strPk = params[jss::public_key].asString(); - pk = parseBase58(TokenType::AccountPublic, strPk); - - if (!pk) - { - auto pkHex = strUnHex(strPk); - if (!pkHex) - return rpcError(rpcPUBLIC_MALFORMED); - auto const pkType = publicKeyType(makeSlice(*pkHex)); - if (!pkType) - return rpcError(rpcPUBLIC_MALFORMED); - pk.emplace(makeSlice(*pkHex)); - } - } - - uint256 channelId; - if (!channelId.parseHex(params[jss::channel_id].asString())) - return rpcError(rpcCHANNEL_MALFORMED); - - std::optional const optDrops = - params[jss::amount].isString() ? to_uint64(params[jss::amount].asString()) : std::nullopt; - - if (!optDrops) - return rpcError(rpcCHANNEL_AMT_MALFORMED); - - std::uint64_t const drops = *optDrops; - - auto sig = strUnHex(params[jss::signature].asString()); - if (!sig || !sig->size()) - return rpcError(rpcINVALID_PARAMS); - - Serializer msg; - serializePayChanAuthorization(msg, channelId, XRPAmount(drops)); - - Json::Value result; - result[jss::signature_verified] = verify(*pk, msg.slice(), makeSlice(*sig)); - return result; -} - } // namespace xrpl diff --git a/src/xrpld/rpc/handlers/SignHandler.cpp b/src/xrpld/rpc/handlers/admin/signing/Sign.cpp similarity index 100% rename from src/xrpld/rpc/handlers/SignHandler.cpp rename to src/xrpld/rpc/handlers/admin/signing/Sign.cpp diff --git a/src/xrpld/rpc/handlers/SignFor.cpp b/src/xrpld/rpc/handlers/admin/signing/SignFor.cpp similarity index 100% rename from src/xrpld/rpc/handlers/SignFor.cpp rename to src/xrpld/rpc/handlers/admin/signing/SignFor.cpp diff --git a/src/xrpld/rpc/handlers/ConsensusInfo.cpp b/src/xrpld/rpc/handlers/admin/status/ConsensusInfo.cpp similarity index 100% rename from src/xrpld/rpc/handlers/ConsensusInfo.cpp rename to src/xrpld/rpc/handlers/admin/status/ConsensusInfo.cpp diff --git a/src/xrpld/rpc/handlers/FetchInfo.cpp b/src/xrpld/rpc/handlers/admin/status/FetchInfo.cpp similarity index 100% rename from src/xrpld/rpc/handlers/FetchInfo.cpp rename to src/xrpld/rpc/handlers/admin/status/FetchInfo.cpp diff --git a/src/xrpld/rpc/handlers/GetCounts.cpp b/src/xrpld/rpc/handlers/admin/status/GetCounts.cpp similarity index 96% rename from src/xrpld/rpc/handlers/GetCounts.cpp rename to src/xrpld/rpc/handlers/admin/status/GetCounts.cpp index f5f953046d..648d29a5fd 100644 --- a/src/xrpld/rpc/handlers/GetCounts.cpp +++ b/src/xrpld/rpc/handlers/admin/status/GetCounts.cpp @@ -71,7 +71,7 @@ getCountsJson(Application& app, int minObjectCount) ret[jss::dbKBTransaction] = dbKB; { - std::size_t c = app.getOPs().getLocalTxCount(); + std::size_t const c = app.getOPs().getLocalTxCount(); if (c > 0) ret[jss::local_txs] = static_cast(c); } @@ -80,7 +80,7 @@ getCountsJson(Application& app, int minObjectCount) ret[jss::write_load] = app.getNodeStore().getWriteLoad(); ret[jss::historical_perminute] = static_cast(app.getInboundLedgers().fetchRate()); - ret[jss::SLE_hit_rate] = app.cachedSLEs().rate(); + ret[jss::SLE_hit_rate] = app.getCachedSLEs().rate(); ret[jss::ledger_hit_rate] = app.getLedgerMaster().getCacheHitRate(); ret[jss::AL_size] = Json::UInt(app.getAcceptedLedgerCache().size()); ret[jss::AL_hit_rate] = app.getAcceptedLedgerCache().getHitRate(); diff --git a/src/xrpld/rpc/handlers/GetCounts.h b/src/xrpld/rpc/handlers/admin/status/GetCounts.h similarity index 86% rename from src/xrpld/rpc/handlers/GetCounts.h rename to src/xrpld/rpc/handlers/admin/status/GetCounts.h index 60c7a3b693..286ad38af3 100644 --- a/src/xrpld/rpc/handlers/GetCounts.h +++ b/src/xrpld/rpc/handlers/admin/status/GetCounts.h @@ -7,4 +7,4 @@ namespace xrpl { Json::Value getCountsJson(Application& app, int minObjectCount); -} +} // namespace xrpl diff --git a/src/xrpld/rpc/handlers/Print.cpp b/src/xrpld/rpc/handlers/admin/status/Print.cpp similarity index 100% rename from src/xrpld/rpc/handlers/Print.cpp rename to src/xrpld/rpc/handlers/admin/status/Print.cpp diff --git a/src/xrpld/rpc/handlers/ValidatorInfo.cpp b/src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp similarity index 77% rename from src/xrpld/rpc/handlers/ValidatorInfo.cpp rename to src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp index 4b6663b68a..aec9dba4b4 100644 --- a/src/xrpld/rpc/handlers/ValidatorInfo.cpp +++ b/src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp @@ -21,7 +21,7 @@ doValidatorInfo(RPC::JsonContext& context) Json::Value ret; // assume validationPK is ephemeral key, get master key - auto const mk = context.app.validatorManifests().getMasterKey(*validationPK); + auto const mk = context.app.getValidatorManifests().getMasterKey(*validationPK); ret[jss::master_key] = toBase58(TokenType::NodePublic, mk); // validationPK is master key, this implies that there is no ephemeral @@ -31,13 +31,13 @@ doValidatorInfo(RPC::JsonContext& context) ret[jss::ephemeral_key] = toBase58(TokenType::NodePublic, *validationPK); - if (auto const manifest = context.app.validatorManifests().getManifest(mk)) + if (auto const manifest = context.app.getValidatorManifests().getManifest(mk)) ret[jss::manifest] = base64_encode(*manifest); - if (auto const seq = context.app.validatorManifests().getSequence(mk)) + if (auto const seq = context.app.getValidatorManifests().getSequence(mk)) ret[jss::seq] = *seq; - if (auto const domain = context.app.validatorManifests().getDomain(mk)) + if (auto const domain = context.app.getValidatorManifests().getDomain(mk)) ret[jss::domain] = *domain; return ret; diff --git a/src/xrpld/rpc/handlers/ValidatorListSites.cpp b/src/xrpld/rpc/handlers/admin/status/ValidatorListSites.cpp similarity index 82% rename from src/xrpld/rpc/handlers/ValidatorListSites.cpp rename to src/xrpld/rpc/handlers/admin/status/ValidatorListSites.cpp index 7acaa27168..36e2064387 100644 --- a/src/xrpld/rpc/handlers/ValidatorListSites.cpp +++ b/src/xrpld/rpc/handlers/admin/status/ValidatorListSites.cpp @@ -9,7 +9,7 @@ namespace xrpl { Json::Value doValidatorListSites(RPC::JsonContext& context) { - return context.app.validatorSites().getJson(); + return context.app.getValidatorSites().getJson(); } } // namespace xrpl diff --git a/src/xrpld/rpc/handlers/Validators.cpp b/src/xrpld/rpc/handlers/admin/status/Validators.cpp similarity index 83% rename from src/xrpld/rpc/handlers/Validators.cpp rename to src/xrpld/rpc/handlers/admin/status/Validators.cpp index a83a6fe4b0..95e6de9e68 100644 --- a/src/xrpld/rpc/handlers/Validators.cpp +++ b/src/xrpld/rpc/handlers/admin/status/Validators.cpp @@ -9,7 +9,7 @@ namespace xrpl { Json::Value doValidators(RPC::JsonContext& context) { - return context.app.validators().getJson(); + return context.app.getValidators().getJson(); } } // namespace xrpl diff --git a/src/xrpld/rpc/handlers/LedgerHandler.cpp b/src/xrpld/rpc/handlers/ledger/Ledger.cpp similarity index 89% rename from src/xrpld/rpc/handlers/LedgerHandler.cpp rename to src/xrpld/rpc/handlers/ledger/Ledger.cpp index 6d188f8520..6bf627d29c 100644 --- a/src/xrpld/rpc/handlers/LedgerHandler.cpp +++ b/src/xrpld/rpc/handlers/ledger/Ledger.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -21,7 +21,7 @@ Status LedgerHandler::check() { auto const& params = context_.params; - bool needsLedger = params.isMember(jss::ledger) || params.isMember(jss::ledger_hash) || + bool const needsLedger = params.isMember(jss::ledger) || params.isMember(jss::ledger_hash) || params.isMember(jss::ledger_index); if (!needsLedger) return Status::OK; @@ -103,7 +103,7 @@ LedgerHandler::writeResult(Json::Value& value) "and update your request. Field `type` is deprecated."; } - if (warnings.size()) + if (warnings.size() != 0u) value[jss::warnings] = std::move(warnings); } @@ -113,9 +113,9 @@ std::pair doLedgerGrpc(RPC::GRPCContext& context) { auto begin = std::chrono::system_clock::now(); - org::xrpl::rpc::v1::GetLedgerRequest& request = context.params; + org::xrpl::rpc::v1::GetLedgerRequest const& request = context.params; org::xrpl::rpc::v1::GetLedgerResponse response; - grpc::Status status = grpc::Status::OK; + grpc::Status const status = grpc::Status::OK; std::shared_ptr ledger; if (auto status = RPC::ledgerFromRequest(ledger, context)) @@ -147,11 +147,11 @@ doLedgerGrpc(RPC::GRPCContext& context) if (request.expand()) { auto txn = response.mutable_transactions_list()->add_transactions(); - Serializer sTxn = i.first->getSerializer(); + Serializer const sTxn = i.first->getSerializer(); txn->set_transaction_blob(sTxn.data(), sTxn.getLength()); if (i.second) { - Serializer sMeta = i.second->getSerializer(); + Serializer const sMeta = i.second->getSerializer(); txn->set_metadata_blob(sMeta.data(), sMeta.getLength()); } } @@ -173,30 +173,32 @@ doLedgerGrpc(RPC::GRPCContext& context) if (request.get_objects()) { - std::shared_ptr parent = + std::shared_ptr const parent = context.app.getLedgerMaster().getLedgerBySeq(ledger->seq() - 1); - std::shared_ptr base = std::dynamic_pointer_cast(parent); + std::shared_ptr const base = std::dynamic_pointer_cast(parent); if (!base) { - grpc::Status errorStatus{grpc::StatusCode::NOT_FOUND, "parent ledger not validated"}; + grpc::Status const errorStatus{ + grpc::StatusCode::NOT_FOUND, "parent ledger not validated"}; return {response, errorStatus}; } - std::shared_ptr desired = std::dynamic_pointer_cast(ledger); + std::shared_ptr const desired = + std::dynamic_pointer_cast(ledger); if (!desired) { - grpc::Status errorStatus{grpc::StatusCode::NOT_FOUND, "ledger not validated"}; + grpc::Status const errorStatus{grpc::StatusCode::NOT_FOUND, "ledger not validated"}; return {response, errorStatus}; } SHAMap::Delta differences; - int maxDifferences = std::numeric_limits::max(); + int const maxDifferences = std::numeric_limits::max(); - bool res = base->stateMap().compare(desired->stateMap(), differences, maxDifferences); + bool const res = base->stateMap().compare(desired->stateMap(), differences, maxDifferences); if (!res) { - grpc::Status errorStatus{ + grpc::Status const errorStatus{ grpc::StatusCode::RESOURCE_EXHAUSTED, "too many differences between specified ledgers"}; return {response, errorStatus}; @@ -215,11 +217,17 @@ doLedgerGrpc(RPC::GRPCContext& context) obj->set_data(inDesired->data(), inDesired->size()); } if (inBase && inDesired) + { obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::MODIFIED); + } else if (inBase && !inDesired) + { obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::DELETED); + } else + { obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::CREATED); + } auto const blob = inDesired ? inDesired->slice() : inBase->slice(); auto const objectType = static_cast(blob[1] << 8 | blob[2]); diff --git a/src/xrpld/rpc/handlers/LedgerHandler.h b/src/xrpld/rpc/handlers/ledger/Ledger.h similarity index 98% rename from src/xrpld/rpc/handlers/LedgerHandler.h rename to src/xrpld/rpc/handlers/ledger/Ledger.h index 418958e9f1..f024241546 100644 --- a/src/xrpld/rpc/handlers/LedgerHandler.h +++ b/src/xrpld/rpc/handlers/ledger/Ledger.h @@ -14,7 +14,7 @@ namespace Json { class Object; -} +} // namespace Json namespace xrpl { namespace RPC { diff --git a/src/xrpld/rpc/handlers/LedgerClosed.cpp b/src/xrpld/rpc/handlers/ledger/LedgerClosed.cpp similarity index 100% rename from src/xrpld/rpc/handlers/LedgerClosed.cpp rename to src/xrpld/rpc/handlers/ledger/LedgerClosed.cpp diff --git a/src/xrpld/rpc/handlers/LedgerCurrent.cpp b/src/xrpld/rpc/handlers/ledger/LedgerCurrent.cpp similarity index 100% rename from src/xrpld/rpc/handlers/LedgerCurrent.cpp rename to src/xrpld/rpc/handlers/ledger/LedgerCurrent.cpp diff --git a/src/xrpld/rpc/handlers/LedgerData.cpp b/src/xrpld/rpc/handlers/ledger/LedgerData.cpp similarity index 94% rename from src/xrpld/rpc/handlers/LedgerData.cpp rename to src/xrpld/rpc/handlers/ledger/LedgerData.cpp index 308f4b4436..059c844e6e 100644 --- a/src/xrpld/rpc/handlers/LedgerData.cpp +++ b/src/xrpld/rpc/handlers/ledger/LedgerData.cpp @@ -116,9 +116,9 @@ doLedgerData(RPC::JsonContext& context) std::pair doLedgerDataGrpc(RPC::GRPCContext& context) { - org::xrpl::rpc::v1::GetLedgerDataRequest& request = context.params; + org::xrpl::rpc::v1::GetLedgerDataRequest const& request = context.params; org::xrpl::rpc::v1::GetLedgerDataResponse response; - grpc::Status status = grpc::Status::OK; + grpc::Status const status = grpc::Status::OK; std::shared_ptr ledger; if (auto status = RPC::ledgerFromRequest(ledger, context)) @@ -140,14 +140,14 @@ doLedgerDataGrpc(RPC::GRPCContext& con { startKey = *key; } - else if (request.marker().size() != 0) + else if (!request.marker().empty()) { - grpc::Status errorStatus{grpc::StatusCode::INVALID_ARGUMENT, "marker malformed"}; + grpc::Status const errorStatus{grpc::StatusCode::INVALID_ARGUMENT, "marker malformed"}; return {response, errorStatus}; } auto e = ledger->sles.end(); - if (request.end_marker().size() != 0) + if (!request.end_marker().empty()) { auto const key = uint256::fromVoidChecked(request.end_marker()); diff --git a/src/xrpld/rpc/handlers/LedgerDiff.cpp b/src/xrpld/rpc/handlers/ledger/LedgerDiff.cpp similarity index 72% rename from src/xrpld/rpc/handlers/LedgerDiff.cpp rename to src/xrpld/rpc/handlers/ledger/LedgerDiff.cpp index 2990315880..97c4efcc7a 100644 --- a/src/xrpld/rpc/handlers/LedgerDiff.cpp +++ b/src/xrpld/rpc/handlers/ledger/LedgerDiff.cpp @@ -5,50 +5,50 @@ namespace xrpl { std::pair doLedgerDiffGrpc(RPC::GRPCContext& context) { - org::xrpl::rpc::v1::GetLedgerDiffRequest& request = context.params; + org::xrpl::rpc::v1::GetLedgerDiffRequest const& request = context.params; org::xrpl::rpc::v1::GetLedgerDiffResponse response; - grpc::Status status = grpc::Status::OK; + grpc::Status const status = grpc::Status::OK; std::shared_ptr baseLedgerRv; std::shared_ptr desiredLedgerRv; if (RPC::ledgerFromSpecifier(baseLedgerRv, request.base_ledger(), context)) { - grpc::Status errorStatus{grpc::StatusCode::NOT_FOUND, "base ledger not found"}; + grpc::Status const errorStatus{grpc::StatusCode::NOT_FOUND, "base ledger not found"}; return {response, errorStatus}; } if (RPC::ledgerFromSpecifier(desiredLedgerRv, request.desired_ledger(), context)) { - grpc::Status errorStatus{grpc::StatusCode::NOT_FOUND, "desired ledger not found"}; + grpc::Status const errorStatus{grpc::StatusCode::NOT_FOUND, "desired ledger not found"}; return {response, errorStatus}; } - std::shared_ptr baseLedger = + std::shared_ptr const baseLedger = std::dynamic_pointer_cast(baseLedgerRv); if (!baseLedger) { - grpc::Status errorStatus{grpc::StatusCode::NOT_FOUND, "base ledger not validated"}; + grpc::Status const errorStatus{grpc::StatusCode::NOT_FOUND, "base ledger not validated"}; return {response, errorStatus}; } - std::shared_ptr desiredLedger = + std::shared_ptr const desiredLedger = std::dynamic_pointer_cast(desiredLedgerRv); if (!desiredLedger) { - grpc::Status errorStatus{grpc::StatusCode::NOT_FOUND, "base ledger not validated"}; + grpc::Status const errorStatus{grpc::StatusCode::NOT_FOUND, "desired ledger not validated"}; return {response, errorStatus}; } SHAMap::Delta differences; - int maxDifferences = std::numeric_limits::max(); + int const maxDifferences = std::numeric_limits::max(); - bool res = + bool const res = baseLedger->stateMap().compare(desiredLedger->stateMap(), differences, maxDifferences); if (!res) { - grpc::Status errorStatus{ + grpc::Status const errorStatus{ grpc::StatusCode::RESOURCE_EXHAUSTED, "too many differences between specified ledgers"}; return {response, errorStatus}; } diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp similarity index 97% rename from src/xrpld/rpc/handlers/LedgerEntry.cpp rename to src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp index e4e8f52fd7..377983718e 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp @@ -1,14 +1,14 @@ #include #include #include -#include +#include #include #include #include #include -#include #include +#include #include #include #include @@ -70,9 +70,11 @@ parseIndex(Json::Value const& params, Json::StaticString const fieldName, unsign if (index == jss::nunl) return keylet::negativeUNL().key; if (index == jss::hashes) + { // Note this only finds the "short" skip list. Use "hashes":index to // get the long list. return keylet::skip().key; + } } return parseObjectID(params, fieldName, "hex string"); } @@ -326,7 +328,7 @@ parseDepositPreauth( "malformedAuthorizedCredentials", jss::authorized_credentials, "array"); } - return keylet::depositPreauth(*owner, std::move(sorted)).key; + return keylet::depositPreauth(*owner, sorted).key; } static Expected @@ -368,7 +370,7 @@ parseDirectoryNode( "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields."); } - std::uint64_t uSubIndex = params.get(jss::sub_index, 0).asUInt(); + std::uint64_t const uSubIndex = params.get(jss::sub_index, 0).asUInt(); if (params.isMember(jss::dir_root)) { @@ -532,8 +534,10 @@ parseMPTokenIssuance( { auto const mptIssuanceID = LedgerEntryHelpers::parse(params); if (!mptIssuanceID) + { return LedgerEntryHelpers::invalidFieldError( "malformedMPTokenIssuance", fieldName, "Hash192"); + } return keylet::mptIssuance(*mptIssuanceID).key; } @@ -901,8 +905,8 @@ doLedgerEntry(RPC::JsonContext& context) // this exception return an invalidParam error. return RPC::make_error(rpcINVALID_PARAMS); } - else - throw; + + throw; } // Return the computed index regardless of whether the node exists. @@ -952,9 +956,9 @@ doLedgerEntry(RPC::JsonContext& context) std::pair doLedgerEntryGrpc(RPC::GRPCContext& context) { - org::xrpl::rpc::v1::GetLedgerEntryRequest& request = context.params; + org::xrpl::rpc::v1::GetLedgerEntryRequest const& request = context.params; org::xrpl::rpc::v1::GetLedgerEntryResponse response; - grpc::Status status = grpc::Status::OK; + grpc::Status const status = grpc::Status::OK; std::shared_ptr ledger; if (auto status = RPC::ledgerFromRequest(ledger, context)) @@ -974,14 +978,14 @@ doLedgerEntryGrpc(RPC::GRPCContext& c auto const key = uint256::fromVoidChecked(request.key()); if (!key) { - grpc::Status errorStatus{grpc::StatusCode::INVALID_ARGUMENT, "index malformed"}; + grpc::Status const errorStatus{grpc::StatusCode::INVALID_ARGUMENT, "index malformed"}; return {response, errorStatus}; } auto const sleNode = ledger->read(keylet::unchecked(*key)); if (!sleNode) { - grpc::Status errorStatus{grpc::StatusCode::NOT_FOUND, "object not found"}; + grpc::Status const errorStatus{grpc::StatusCode::NOT_FOUND, "object not found"}; return {response, errorStatus}; } diff --git a/src/xrpld/rpc/handlers/LedgerEntryHelpers.h b/src/xrpld/rpc/handlers/ledger/LedgerEntryHelpers.h similarity index 88% rename from src/xrpld/rpc/handlers/LedgerEntryHelpers.h rename to src/xrpld/rpc/handlers/ledger/LedgerEntryHelpers.h index 683e6ebb5a..9c5f0b2fcb 100644 --- a/src/xrpld/rpc/handlers/LedgerEntryHelpers.h +++ b/src/xrpld/rpc/handlers/ledger/LedgerEntryHelpers.h @@ -16,29 +16,27 @@ namespace xrpl { namespace LedgerEntryHelpers { -Unexpected +inline Unexpected missingFieldError(Json::StaticString const field, std::optional err = std::nullopt) { Json::Value json = Json::objectValue; - auto error = RPC::missing_field_message(std::string(field.c_str())); json[jss::error] = err.value_or("malformedRequest"); json[jss::error_code] = rpcINVALID_PARAMS; - json[jss::error_message] = std::move(error); + json[jss::error_message] = RPC::missing_field_message(std::string(field.c_str())); return Unexpected(json); } -Unexpected +inline Unexpected invalidFieldError(std::string const& err, Json::StaticString const field, std::string const& type) { Json::Value json = Json::objectValue; - auto error = RPC::expected_field_message(field, type); json[jss::error] = err; json[jss::error_code] = rpcINVALID_PARAMS; - json[jss::error_message] = std::move(error); + json[jss::error_message] = RPC::expected_field_message(field, type); return Unexpected(json); } -Unexpected +inline Unexpected malformedError(std::string const& err, std::string const& message) { Json::Value json = Json::objectValue; @@ -48,7 +46,7 @@ malformedError(std::string const& err, std::string const& message) return Unexpected(json); } -Expected +inline Expected hasRequired( Json::Value const& params, std::initializer_list fields, @@ -88,7 +86,7 @@ required( } template <> -std::optional +inline std::optional parse(Json::Value const& param) { if (!param.isString()) @@ -103,7 +101,7 @@ parse(Json::Value const& param) return account; } -Expected +inline Expected requiredAccountID( Json::Value const& params, Json::StaticString const fieldName, @@ -112,20 +110,20 @@ requiredAccountID( return required(params, fieldName, err, "AccountID"); } -std::optional +inline std::optional parseHexBlob(Json::Value const& param, std::size_t maxLength) { if (!param.isString()) return std::nullopt; - auto const blob = strUnHex(param.asString()); + auto blob = strUnHex(param.asString()); if (!blob || blob->empty() || blob->size() > maxLength) return std::nullopt; return blob; } -Expected +inline Expected requiredHexBlob( Json::Value const& params, Json::StaticString const fieldName, @@ -144,7 +142,7 @@ requiredHexBlob( } template <> -std::optional +inline std::optional parse(Json::Value const& param) { if (param.isUInt() || (param.isInt() && param.asInt() >= 0)) @@ -152,7 +150,7 @@ parse(Json::Value const& param) if (param.isString()) { - std::uint32_t v; + std::uint32_t v = 0; if (beast::lexicalCastChecked(v, param.asString())) return v; } @@ -160,7 +158,7 @@ parse(Json::Value const& param) return std::nullopt; } -Expected +inline Expected requiredUInt32( Json::Value const& params, Json::StaticString const fieldName, @@ -170,7 +168,7 @@ requiredUInt32( } template <> -std::optional +inline std::optional parse(Json::Value const& param) { uint256 uNodeIndex; @@ -182,7 +180,7 @@ parse(Json::Value const& param) return uNodeIndex; } -Expected +inline Expected requiredUInt256( Json::Value const& params, Json::StaticString const fieldName, @@ -192,7 +190,7 @@ requiredUInt256( } template <> -std::optional +inline std::optional parse(Json::Value const& param) { uint192 field; @@ -204,7 +202,7 @@ parse(Json::Value const& param) return field; } -Expected +inline Expected requiredUInt192( Json::Value const& params, Json::StaticString const fieldName, @@ -214,7 +212,7 @@ requiredUInt192( } template <> -std::optional +inline std::optional parse(Json::Value const& param) { try @@ -227,13 +225,13 @@ parse(Json::Value const& param) } } -Expected +inline Expected requiredIssue(Json::Value const& params, Json::StaticString const fieldName, std::string const& err) { return required(params, fieldName, err, "Issue"); } -Expected +inline Expected parseBridgeFields(Json::Value const& params) { if (auto const value = hasRequired( diff --git a/src/xrpld/rpc/handlers/LedgerHeader.cpp b/src/xrpld/rpc/handlers/ledger/LedgerHeader.cpp similarity index 100% rename from src/xrpld/rpc/handlers/LedgerHeader.cpp rename to src/xrpld/rpc/handlers/ledger/LedgerHeader.cpp diff --git a/src/xrpld/rpc/handlers/AMMInfo.cpp b/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp similarity index 95% rename from src/xrpld/rpc/handlers/AMMInfo.cpp rename to src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp index 4e91c9c308..cdd70a5cd8 100644 --- a/src/xrpld/rpc/handlers/AMMInfo.cpp +++ b/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp @@ -2,11 +2,12 @@ #include #include +#include #include #include +#include #include #include -#include #include @@ -74,17 +75,25 @@ doAMMInfo(RPC::JsonContext& context) if (params.isMember(jss::asset)) { if (auto const i = getIssue(params[jss::asset], context.j)) + { issue1 = *i; + } else + { return Unexpected(i.error()); + } } if (params.isMember(jss::asset2)) { if (auto const i = getIssue(params[jss::asset2], context.j)) + { issue2 = *i; + } else + { return Unexpected(i.error()); + } } if (params.isMember(jss::amm_account)) @@ -130,7 +139,7 @@ doAMMInfo(RPC::JsonContext& context) issue2 = (*amm)[sfAsset2].get(); } - return ValuesFromContextParams{accountID, *issue1, *issue2, std::move(amm)}; + return ValuesFromContextParams{accountID, *issue1, *issue2, amm}; }; auto const r = getValuesFromContextParams(); @@ -175,7 +184,7 @@ doAMMInfo(RPC::JsonContext& context) "xrpl::doAMMInfo : auction slot is set"); if (amm->isFieldPresent(sfAuctionSlot)) { - auto const& auctionSlot = static_cast(amm->peekAtField(sfAuctionSlot)); + auto const& auctionSlot = safe_downcast(amm->peekAtField(sfAuctionSlot)); if (auctionSlot.isFieldPresent(sfAccount)) { Json::Value auction; @@ -203,11 +212,15 @@ doAMMInfo(RPC::JsonContext& context) } if (!isXRP(asset1Balance)) + { ammResult[jss::asset_frozen] = isFrozen(*ledger, ammAccountID, issue1.currency, issue1.account); + } if (!isXRP(asset2Balance)) + { ammResult[jss::asset2_frozen] = isFrozen(*ledger, ammAccountID, issue2.currency, issue2.account); + } result[jss::amm] = std::move(ammResult); if (!result.isMember(jss::ledger_index) && !result.isMember(jss::ledger_hash)) diff --git a/src/xrpld/rpc/handlers/orderbook/BookChanges.cpp b/src/xrpld/rpc/handlers/orderbook/BookChanges.cpp new file mode 100644 index 0000000000..83b26729a1 --- /dev/null +++ b/src/xrpld/rpc/handlers/orderbook/BookChanges.cpp @@ -0,0 +1,21 @@ +#include +#include +#include + +#include + +namespace xrpl { + +Json::Value +doBookChanges(RPC::JsonContext& context) +{ + std::shared_ptr ledger; + + Json::Value result = RPC::lookupLedger(ledger, context); + if (ledger == nullptr) + return result; + + return RPC::computeBookChanges(ledger); +} + +} // namespace xrpl diff --git a/src/xrpld/rpc/handlers/BookOffers.cpp b/src/xrpld/rpc/handlers/orderbook/BookOffers.cpp similarity index 94% rename from src/xrpld/rpc/handlers/BookOffers.cpp rename to src/xrpld/rpc/handlers/orderbook/BookOffers.cpp index 070d7988de..33d1e938ad 100644 --- a/src/xrpld/rpc/handlers/BookOffers.cpp +++ b/src/xrpld/rpc/handlers/orderbook/BookOffers.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -83,12 +82,16 @@ doBookOffers(RPC::JsonContext& context) return RPC::expected_field_error("taker_pays.issuer", "string"); if (!to_issuer(pay_issuer, taker_pays[jss::issuer].asString())) + { return RPC::make_error( rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', bad issuer."); + } if (pay_issuer == noAccount()) + { return RPC::make_error( rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', bad issuer account one."); + } } else { @@ -96,14 +99,18 @@ doBookOffers(RPC::JsonContext& context) } if (isXRP(pay_currency) && !isXRP(pay_issuer)) + { return RPC::make_error( rpcSRC_ISR_MALFORMED, "Unneeded field 'taker_pays.issuer' for " "XRP currency specification."); + } if (!isXRP(pay_currency) && isXRP(pay_issuer)) + { return RPC::make_error( rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', expected non-XRP issuer."); + } AccountID get_issuer; @@ -113,12 +120,16 @@ doBookOffers(RPC::JsonContext& context) return RPC::expected_field_error("taker_gets.issuer", "string"); if (!to_issuer(get_issuer, taker_gets[jss::issuer].asString())) + { return RPC::make_error( rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer."); + } if (get_issuer == noAccount()) + { return RPC::make_error( rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer account one."); + } } else { @@ -126,14 +137,18 @@ doBookOffers(RPC::JsonContext& context) } if (isXRP(get_currency) && !isXRP(get_issuer)) + { return RPC::make_error( rpcDST_ISR_MALFORMED, "Unneeded field 'taker_gets.issuer' for " "XRP currency specification."); + } if (!isXRP(get_currency) && isXRP(get_issuer)) + { return RPC::make_error( rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', expected non-XRP issuer."); + } std::optional takerID; if (context.params.isMember(jss::taker)) @@ -155,10 +170,8 @@ doBookOffers(RPC::JsonContext& context) { return RPC::make_error(rpcDOMAIN_MALFORMED, "Unable to parse domain."); } - else - { - domain = num; - } + + domain = num; } if (pay_currency == get_currency && pay_issuer == get_issuer) @@ -167,7 +180,7 @@ doBookOffers(RPC::JsonContext& context) return RPC::make_error(rpcBAD_MARKET); } - unsigned int limit; + unsigned int limit = 0; if (auto err = readLimitField(limit, RPC::Tuning::bookOffers, context)) return *err; @@ -191,16 +204,4 @@ doBookOffers(RPC::JsonContext& context) return jvResult; } -Json::Value -doBookChanges(RPC::JsonContext& context) -{ - std::shared_ptr ledger; - - Json::Value result = RPC::lookupLedger(ledger, context); - if (ledger == nullptr) - return result; - - return RPC::computeBookChanges(ledger); -} - } // namespace xrpl diff --git a/src/xrpld/rpc/handlers/DepositAuthorized.cpp b/src/xrpld/rpc/handlers/orderbook/DepositAuthorized.cpp similarity index 93% rename from src/xrpld/rpc/handlers/DepositAuthorized.cpp rename to src/xrpld/rpc/handlers/orderbook/DepositAuthorized.cpp index 7bad908c51..fb1c0db884 100644 --- a/src/xrpld/rpc/handlers/DepositAuthorized.cpp +++ b/src/xrpld/rpc/handlers/orderbook/DepositAuthorized.cpp @@ -1,8 +1,8 @@ #include #include -#include #include +#include #include #include #include @@ -27,25 +27,29 @@ doDepositAuthorized(RPC::JsonContext& context) if (!params.isMember(jss::source_account)) return RPC::missing_field_error(jss::source_account); if (!params[jss::source_account].isString()) + { return RPC::make_error( rpcINVALID_PARAMS, RPC::expected_field_message(jss::source_account, "a string")); + } auto srcID = parseBase58(params[jss::source_account].asString()); if (!srcID) return rpcError(rpcACT_MALFORMED); - auto const srcAcct{std::move(srcID.value())}; + auto const srcAcct{srcID.value()}; // Validate destination_account. if (!params.isMember(jss::destination_account)) return RPC::missing_field_error(jss::destination_account); if (!params[jss::destination_account].isString()) + { return RPC::make_error( rpcINVALID_PARAMS, RPC::expected_field_message(jss::destination_account, "a string")); + } auto dstID = parseBase58(params[jss::destination_account].asString()); if (!dstID) return rpcError(rpcACT_MALFORMED); - auto const dstAcct{std::move(dstID.value())}; + auto const dstAcct{dstID.value()}; // Validate ledger. std::shared_ptr ledger; @@ -69,7 +73,7 @@ doDepositAuthorized(RPC::JsonContext& context) return result; } - bool const reqAuth = (sleDest->getFlags() & lsfDepositAuth) && (srcAcct != dstAcct); + bool const reqAuth = ((sleDest->getFlags() & lsfDepositAuth) != 0u) && (srcAcct != dstAcct); bool const credentialsPresent = params.isMember(jss::credentials); std::set> sorted; @@ -84,7 +88,7 @@ doDepositAuthorized(RPC::JsonContext& context) RPC::expected_field_message( jss::credentials, "is non-empty array of CredentialID(hash256)")); } - else if (creds.size() > maxCredentialsArraySize) + if (creds.size() > maxCredentialsArraySize) { return RPC::make_error( rpcINVALID_PARAMS, RPC::expected_field_message(jss::credentials, "array too long")); @@ -118,7 +122,7 @@ doDepositAuthorized(RPC::JsonContext& context) return result; } - if (!(sleCred->getFlags() & lsfAccepted)) + if ((sleCred->getFlags() & lsfAccepted) == 0u) { RPC::inject_error(rpcBAD_CREDENTIALS, "credentials aren't accepted", result); return result; @@ -151,8 +155,10 @@ doDepositAuthorized(RPC::JsonContext& context) // not set, then the deposit should be fine. bool depositAuthorized = true; if (reqAuth) + { depositAuthorized = ledger->exists(keylet::depositPreauth(dstAcct, srcAcct)) || (credentialsPresent && ledger->exists(keylet::depositPreauth(dstAcct, sorted))); + } result[jss::source_account] = params[jss::source_account].asString(); result[jss::destination_account] = params[jss::destination_account].asString(); diff --git a/src/xrpld/rpc/handlers/GetAggregatePrice.cpp b/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp similarity index 93% rename from src/xrpld/rpc/handlers/GetAggregatePrice.cpp rename to src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp index eaeff767cd..281f2d63a7 100644 --- a/src/xrpld/rpc/handlers/GetAggregatePrice.cpp +++ b/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -25,7 +26,7 @@ static void iteratePriceData( RPC::JsonContext& context, std::shared_ptr const& sle, - std::function&& f) + std::function const& f) { using Meta = std::shared_ptr; constexpr std::uint8_t maxHistory = 3; @@ -55,14 +56,14 @@ iteratePriceData( if (prevChain == chain) return; - if (!oracle || f(*oracle) || isNew) + if ((oracle == nullptr) || f(*oracle) || isNew) return; if (++history > maxHistory) return; - uint256 prevTx = chain->getFieldH256(sfPreviousTxnID); - std::uint32_t prevSeq = chain->getFieldU32(sfPreviousTxnLgrSeq); + uint256 const prevTx = chain->getFieldH256(sfPreviousTxnID); + std::uint32_t const prevSeq = chain->getFieldU32(sfPreviousTxnLgrSeq); auto const ledger = context.ledgerMaster.getLedgerBySeq(prevSeq); if (!ledger) @@ -87,8 +88,8 @@ iteratePriceData( if (isNew && history == 1) return; - oracle = isNew ? &static_cast(node.peekAtField(sfNewFields)) - : &static_cast(node.peekAtField(sfFinalFields)); + oracle = isNew ? &safe_downcast(node.peekAtField(sfNewFields)) + : &safe_downcast(node.peekAtField(sfFinalFields)); break; } } @@ -148,7 +149,7 @@ doGetAggregatePrice(RPC::JsonContext& context) // support positive int, uint, and a number represented as a string auto validUInt = [](Json::Value const& params, Json::StaticString const& field) { auto const& jv = params[field]; - std::uint32_t v; + std::uint32_t v = 0; return jv.isUInt() || (jv.isInt() && jv.asInt() >= 0) || (jv.isString() && beast::lexicalCastChecked(v, jv.asString())); }; @@ -217,6 +218,12 @@ doGetAggregatePrice(RPC::JsonContext& context) return result; } + // Get the ledger + std::shared_ptr ledger; + result = RPC::lookupLedger(ledger, context); + if (!ledger) + return result; // LCOV_EXCL_LINE + // Collect the dataset into bimap keyed by lastUpdateTime and // STAmount (Number is int64 and price is uint64) Prices prices; @@ -237,11 +244,6 @@ doGetAggregatePrice(RPC::JsonContext& context) return result; } - std::shared_ptr ledger; - result = RPC::lookupLedger(ledger, context); - if (!ledger) - return result; // LCOV_EXCL_LINE - auto const sle = ledger->read(keylet::oracle(*account, *documentID)); iteratePriceData(context, sle, [&](STObject const& node) { auto const& series = node.getFieldArray(sfPriceDataSeries); @@ -283,8 +285,8 @@ doGetAggregatePrice(RPC::JsonContext& context) if (auto const threshold = std::get(timeThreshold)) { // threshold defines an acceptable range {max,min} of lastUpdateTime as - // {latestTime, latestTime - threshold}, the prices with lastUpdateTime - // greater than (latestTime - threshold) are erased. + // {latestTime, latestTime - threshold}. Prices with lastUpdateTime + // less than (latestTime - threshold) are erased (outdated prices). auto const oldestTime = prices.left.rbegin()->first; auto const upperBound = latestTime > threshold ? (latestTime - threshold) : oldestTime; if (upperBound > oldestTime) @@ -318,7 +320,7 @@ doGetAggregatePrice(RPC::JsonContext& context) auto const middle = size_ / 2; if ((size_ % 2) == 0) { - static STAmount two{noIssue(), 2, 0}; + static STAmount const two{noIssue(), 2, 0}; auto it = itAdvance(prices.right.begin(), middle - 1); auto const& a1 = it->first; auto const& a2 = (++it)->first; diff --git a/src/xrpld/rpc/handlers/orderbook/NFTBuyOffers.cpp b/src/xrpld/rpc/handlers/orderbook/NFTBuyOffers.cpp new file mode 100644 index 0000000000..3ee8935f3a --- /dev/null +++ b/src/xrpld/rpc/handlers/orderbook/NFTBuyOffers.cpp @@ -0,0 +1,24 @@ +#include +#include + +#include +#include +#include + +namespace xrpl { + +Json::Value +doNFTBuyOffers(RPC::JsonContext& context) +{ + if (!context.params.isMember(jss::nft_id)) + return RPC::missing_field_error(jss::nft_id); + + uint256 nftId; + + if (!nftId.parseHex(context.params[jss::nft_id].asString())) + return RPC::invalid_field_error(jss::nft_id); + + return enumerateNFTOffers(context, nftId, keylet::nft_buys(nftId)); +} + +} // namespace xrpl diff --git a/src/xrpld/rpc/handlers/NFTOffers.cpp b/src/xrpld/rpc/handlers/orderbook/NFTOffersHelpers.h similarity index 82% rename from src/xrpld/rpc/handlers/NFTOffers.cpp rename to src/xrpld/rpc/handlers/orderbook/NFTOffersHelpers.h index 51f75bfe4c..8f68aeaadd 100644 --- a/src/xrpld/rpc/handlers/NFTOffers.cpp +++ b/src/xrpld/rpc/handlers/orderbook/NFTOffersHelpers.h @@ -1,3 +1,5 @@ +#pragma once + #include #include #include @@ -6,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -13,7 +16,7 @@ namespace xrpl { -static void +inline void appendNftOfferJson( Application const& app, std::shared_ptr const& offer, @@ -41,10 +44,10 @@ appendNftOfferJson( // limit: integer // optional // marker: opaque // optional, resume previous query // } -static Json::Value +inline Json::Value enumerateNFTOffers(RPC::JsonContext& context, uint256 const& nftId, Keylet const& directory) { - unsigned int limit; + unsigned int limit = 0; if (auto err = readLimitField(limit, RPC::Tuning::nftOffers, context)) return *err; @@ -126,32 +129,4 @@ enumerateNFTOffers(RPC::JsonContext& context, uint256 const& nftId, Keylet const return result; } -Json::Value -doNFTSellOffers(RPC::JsonContext& context) -{ - if (!context.params.isMember(jss::nft_id)) - return RPC::missing_field_error(jss::nft_id); - - uint256 nftId; - - if (!nftId.parseHex(context.params[jss::nft_id].asString())) - return RPC::invalid_field_error(jss::nft_id); - - return enumerateNFTOffers(context, nftId, keylet::nft_sells(nftId)); -} - -Json::Value -doNFTBuyOffers(RPC::JsonContext& context) -{ - if (!context.params.isMember(jss::nft_id)) - return RPC::missing_field_error(jss::nft_id); - - uint256 nftId; - - if (!nftId.parseHex(context.params[jss::nft_id].asString())) - return RPC::invalid_field_error(jss::nft_id); - - return enumerateNFTOffers(context, nftId, keylet::nft_buys(nftId)); -} - } // namespace xrpl diff --git a/src/xrpld/rpc/handlers/orderbook/NFTSellOffers.cpp b/src/xrpld/rpc/handlers/orderbook/NFTSellOffers.cpp new file mode 100644 index 0000000000..9dbd9ef49f --- /dev/null +++ b/src/xrpld/rpc/handlers/orderbook/NFTSellOffers.cpp @@ -0,0 +1,24 @@ +#include +#include + +#include +#include +#include + +namespace xrpl { + +Json::Value +doNFTSellOffers(RPC::JsonContext& context) +{ + if (!context.params.isMember(jss::nft_id)) + return RPC::missing_field_error(jss::nft_id); + + uint256 nftId; + + if (!nftId.parseHex(context.params[jss::nft_id].asString())) + return RPC::invalid_field_error(jss::nft_id); + + return enumerateNFTOffers(context, nftId, keylet::nft_sells(nftId)); +} + +} // namespace xrpl diff --git a/src/xrpld/rpc/handlers/PathFind.cpp b/src/xrpld/rpc/handlers/orderbook/PathFind.cpp similarity index 84% rename from src/xrpld/rpc/handlers/PathFind.cpp rename to src/xrpld/rpc/handlers/orderbook/PathFind.cpp index 357cd9b91e..ced3625b4c 100644 --- a/src/xrpld/rpc/handlers/PathFind.cpp +++ b/src/xrpld/rpc/handlers/orderbook/PathFind.cpp @@ -1,7 +1,7 @@ #include #include -#include #include +#include #include #include @@ -34,13 +34,13 @@ doPathFind(RPC::JsonContext& context) { context.loadType = Resource::feeHeavyBurdenRPC; context.infoSub->clearRequest(); - return context.app.getPathRequests().makePathRequest( + return context.app.getPathRequestManager().makePathRequest( context.infoSub, lpLedger, context.params); } if (sSubCommand == "close") { - InfoSubRequest::pointer request = context.infoSub->getRequest(); + InfoSubRequest::pointer const request = context.infoSub->getRequest(); if (!request) return rpcError(rpcNO_PF_REQUEST); @@ -51,7 +51,7 @@ doPathFind(RPC::JsonContext& context) if (sSubCommand == "status") { - InfoSubRequest::pointer request = context.infoSub->getRequest(); + InfoSubRequest::pointer const request = context.infoSub->getRequest(); if (!request) return rpcError(rpcNO_PF_REQUEST); diff --git a/src/xrpld/rpc/handlers/RipplePathFind.cpp b/src/xrpld/rpc/handlers/orderbook/RipplePathFind.cpp similarity index 93% rename from src/xrpld/rpc/handlers/RipplePathFind.cpp rename to src/xrpld/rpc/handlers/orderbook/RipplePathFind.cpp index 5f73165287..1b19061b9d 100644 --- a/src/xrpld/rpc/handlers/RipplePathFind.cpp +++ b/src/xrpld/rpc/handlers/orderbook/RipplePathFind.cpp @@ -1,7 +1,7 @@ #include -#include #include #include +#include #include #include @@ -94,7 +94,7 @@ doRipplePathFind(RPC::JsonContext& context) // Both of these failure modes are hard to recreate in a unit test // because they are so dependent on inter-thread timing. However // the failure modes can be observed by synchronously (inside the - // rippled source code) shutting down the application. The code to + // xrpld source code) shutting down the application. The code to // do so looks like this: // // context.app.signalStop(); @@ -105,7 +105,7 @@ doRipplePathFind(RPC::JsonContext& context) // JobQueue before letting the thread continue. // // May 2017 - jvResult = context.app.getPathRequests().makeLegacyPathRequest( + jvResult = context.app.getPathRequestManager().makeLegacyPathRequest( request, [&context]() { // Copying the shared_ptr keeps the coroutine alive up @@ -113,7 +113,7 @@ doRipplePathFind(RPC::JsonContext& context) // captured reference could evaporate when we return from // coroCopy->resume(). This is not strictly necessary, but // will make maintenance easier. - std::shared_ptr coroCopy{context.coro}; + std::shared_ptr const coroCopy{context.coro}; if (!coroCopy->post()) { // The post() failed, so we won't get a thread to let @@ -140,11 +140,11 @@ doRipplePathFind(RPC::JsonContext& context) if (!lpLedger) return jvResult; - RPC::LegacyPathFind lpf(isUnlimited(context.role), context.app); + RPC::LegacyPathFind const lpf(isUnlimited(context.role), context.app); if (!lpf.isOk()) return rpcError(rpcTOO_BUSY); - auto result = context.app.getPathRequests().doLegacyPathRequest( + auto result = context.app.getPathRequestManager().doLegacyPathRequest( context.consumer, lpLedger, context.params); for (auto& fieldName : jvResult.getMemberNames()) diff --git a/src/xrpld/rpc/handlers/Feature1.cpp b/src/xrpld/rpc/handlers/server_info/Feature.cpp similarity index 98% rename from src/xrpld/rpc/handlers/Feature1.cpp rename to src/xrpld/rpc/handlers/server_info/Feature.cpp index bd1e501506..24ff0d62b8 100644 --- a/src/xrpld/rpc/handlers/Feature1.cpp +++ b/src/xrpld/rpc/handlers/server_info/Feature.cpp @@ -61,9 +61,13 @@ doFeature(RPC::JsonContext& context) return rpcError(rpcNO_PERMISSION); if (context.params[jss::vetoed].asBool()) + { table.veto(feature); + } else + { table.unVeto(feature); + } } Json::Value jvReply = table.getJson(feature, isAdmin); diff --git a/src/xrpld/rpc/handlers/Fee1.cpp b/src/xrpld/rpc/handlers/server_info/Fee.cpp similarity index 100% rename from src/xrpld/rpc/handlers/Fee1.cpp rename to src/xrpld/rpc/handlers/server_info/Fee.cpp diff --git a/src/xrpld/rpc/handlers/DoManifest.cpp b/src/xrpld/rpc/handlers/server_info/Manifest.cpp similarity index 78% rename from src/xrpld/rpc/handlers/DoManifest.cpp rename to src/xrpld/rpc/handlers/server_info/Manifest.cpp index 5928947e21..ba3461033f 100644 --- a/src/xrpld/rpc/handlers/DoManifest.cpp +++ b/src/xrpld/rpc/handlers/server_info/Manifest.cpp @@ -32,25 +32,25 @@ doManifest(RPC::JsonContext& context) // first attempt to use params as ephemeral key, // if this lookup succeeds master key will be returned, // else an unseated optional is returned - auto const mk = context.app.validatorManifests().getMasterKey(*pk); + auto const mk = context.app.getValidatorManifests().getMasterKey(*pk); - auto const ek = context.app.validatorManifests().getSigningKey(mk); + auto const ek = context.app.getValidatorManifests().getSigningKey(mk); // if ephemeral key not found, we don't have specified manifest if (!ek) return ret; - if (auto const manifest = context.app.validatorManifests().getManifest(mk)) + if (auto const manifest = context.app.getValidatorManifests().getManifest(mk)) ret[jss::manifest] = base64_encode(*manifest); Json::Value details; details[jss::master_key] = toBase58(TokenType::NodePublic, mk); details[jss::ephemeral_key] = toBase58(TokenType::NodePublic, *ek); - if (auto const seq = context.app.validatorManifests().getSequence(mk)) + if (auto const seq = context.app.getValidatorManifests().getSequence(mk)) details[jss::seq] = *seq; - if (auto const domain = context.app.validatorManifests().getDomain(mk)) + if (auto const domain = context.app.getValidatorManifests().getDomain(mk)) details[jss::domain] = *domain; ret[jss::details] = details; diff --git a/src/xrpld/rpc/handlers/ServerDefinitions.cpp b/src/xrpld/rpc/handlers/server_info/ServerDefinitions.cpp similarity index 97% rename from src/xrpld/rpc/handlers/ServerDefinitions.cpp rename to src/xrpld/rpc/handlers/server_info/ServerDefinitions.cpp index e153065ea9..f99f427ca8 100644 --- a/src/xrpld/rpc/handlers/ServerDefinitions.cpp +++ b/src/xrpld/rpc/handlers/server_info/ServerDefinitions.cpp @@ -24,7 +24,7 @@ namespace detail { class ServerDefinitions { private: - std::string + static std::string // translate e.g. STI_LEDGERENTRY to LedgerEntry translate(std::string const& inp); @@ -63,9 +63,11 @@ ServerDefinitions::translate(std::string const& inp) { if (contains("512") || contains("384") || contains("256") || contains("192") || contains("160") || contains("128")) + { return replace("UINT", "Hash"); - else - return replace("UINT", "UInt"); + } + + return replace("UINT", "UInt"); } static std::unordered_map const replacements{ @@ -91,7 +93,7 @@ ServerDefinitions::translate(std::string const& inp) // convert snake_case to CamelCase for (;;) { - pos = inpToProcess.find("_"); + pos = inpToProcess.find('_'); if (pos == std::string::npos) pos = inpToProcess.size(); std::string token = inpToProcess.substr(0, pos); @@ -102,7 +104,9 @@ ServerDefinitions::translate(std::string const& inp) out += token; } else + { out += token; + } if (pos == inpToProcess.size()) break; inpToProcess = inpToProcess.substr(pos + 1); @@ -119,7 +123,7 @@ ServerDefinitions::ServerDefinitions() : defs_{Json::objectValue} std::map typeMap{{-1, "Done"}}; for (auto const& [rawName, typeValue] : sTypeMap) { - std::string typeName = translate(std::string(rawName).substr(4) /* remove STI_ */); + std::string const typeName = translate(std::string(rawName).substr(4) /* remove STI_ */); defs_[jss::TYPES][typeName] = typeValue; typeMap[typeValue] = typeName; } @@ -217,7 +221,7 @@ ServerDefinitions::ServerDefinitions() : defs_{Json::objectValue} for (auto const& [code, field] : xrpl::SField::getKnownCodeToField()) { - if (field->fieldName == "") + if (field->fieldName.empty()) continue; Json::Value innerObj = Json::objectValue; diff --git a/src/xrpld/rpc/handlers/ServerInfo.cpp b/src/xrpld/rpc/handlers/server_info/ServerInfo.cpp similarity index 100% rename from src/xrpld/rpc/handlers/ServerInfo.cpp rename to src/xrpld/rpc/handlers/server_info/ServerInfo.cpp diff --git a/src/xrpld/rpc/handlers/ServerState.cpp b/src/xrpld/rpc/handlers/server_info/ServerState.cpp similarity index 100% rename from src/xrpld/rpc/handlers/ServerState.cpp rename to src/xrpld/rpc/handlers/server_info/ServerState.cpp diff --git a/src/xrpld/rpc/handlers/Version.h b/src/xrpld/rpc/handlers/server_info/Version.h similarity index 100% rename from src/xrpld/rpc/handlers/Version.h rename to src/xrpld/rpc/handlers/server_info/Version.h diff --git a/src/xrpld/rpc/handlers/Subscribe.cpp b/src/xrpld/rpc/handlers/subscribe/Subscribe.cpp similarity index 97% rename from src/xrpld/rpc/handlers/Subscribe.cpp rename to src/xrpld/rpc/handlers/subscribe/Subscribe.cpp index 8fbf5a917e..af3e998a58 100644 --- a/src/xrpld/rpc/handlers/Subscribe.cpp +++ b/src/xrpld/rpc/handlers/subscribe/Subscribe.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -33,7 +32,7 @@ doSubscribe(RPC::JsonContext& context) if (context.role != Role::ADMIN) return rpcError(rpcNO_PERMISSION); - std::string strUrl = context.params[jss::url].asString(); + std::string const strUrl = context.params[jss::url].asString(); std::string strUsername = context.params.isMember(jss::url_username) ? context.params[jss::url_username].asString() : ""; @@ -62,7 +61,7 @@ doSubscribe(RPC::JsonContext& context) strUrl, strUsername, strPassword, - context.app.logs()); + context.app); ispSub = context.netOps.addRpcSub(strUrl, std::dynamic_pointer_cast(rspSub)); } @@ -107,7 +106,7 @@ doSubscribe(RPC::JsonContext& context) if (!it.isString()) return rpcError(rpcSTREAM_MALFORMED); - std::string streamName = it.asString(); + std::string const streamName = it.asString(); if (streamName == "server") { context.netOps.subServer(ispSub, jvResult, context.role == Role::ADMIN); @@ -281,10 +280,8 @@ doSubscribe(RPC::JsonContext& context) { return rpcError(rpcDOMAIN_MALFORMED); } - else - { - book.domain = domain; - } + + book.domain = domain; } if (!isConsistent(book)) diff --git a/src/xrpld/rpc/handlers/Unsubscribe.cpp b/src/xrpld/rpc/handlers/subscribe/Unsubscribe.cpp similarity index 95% rename from src/xrpld/rpc/handlers/Unsubscribe.cpp rename to src/xrpld/rpc/handlers/subscribe/Unsubscribe.cpp index 15da0d1d6a..d3e36cc612 100644 --- a/src/xrpld/rpc/handlers/Unsubscribe.cpp +++ b/src/xrpld/rpc/handlers/subscribe/Unsubscribe.cpp @@ -28,7 +28,7 @@ doUnsubscribe(RPC::JsonContext& context) if (context.role != Role::ADMIN) return rpcError(rpcNO_PERMISSION); - std::string strUrl = context.params[jss::url].asString(); + std::string const strUrl = context.params[jss::url].asString(); ispSub = context.netOps.findRpcSub(strUrl); if (!ispSub) return jvResult; @@ -49,7 +49,7 @@ doUnsubscribe(RPC::JsonContext& context) if (!it.isString()) return rpcError(rpcSTREAM_MALFORMED); - std::string streamName = it.asString(); + std::string const streamName = it.asString(); if (streamName == "server") { context.netOps.unsubServer(ispSub->getSeq()); @@ -165,8 +165,7 @@ doUnsubscribe(RPC::JsonContext& context) return rpcError(rpcSRC_CUR_MALFORMED); } // Parse optional issuer. - else if ( - ((taker_pays.isMember(jss::issuer)) && + if (((taker_pays.isMember(jss::issuer)) && (!taker_pays[jss::issuer].isString() || !to_issuer(book.in.account, taker_pays[jss::issuer].asString()))) // Don't allow illegal issuers. @@ -186,8 +185,7 @@ doUnsubscribe(RPC::JsonContext& context) return rpcError(rpcDST_AMT_MALFORMED); } // Parse optional issuer. - else if ( - ((taker_gets.isMember(jss::issuer)) && + if (((taker_gets.isMember(jss::issuer)) && (!taker_gets[jss::issuer].isString() || !to_issuer(book.out.account, taker_gets[jss::issuer].asString()))) // Don't allow illegal issuers. @@ -211,10 +209,8 @@ doUnsubscribe(RPC::JsonContext& context) { return rpcError(rpcDOMAIN_MALFORMED); } - else - { - book.domain = domain; - } + + book.domain = domain; } context.netOps.unsubBook(ispSub->getSeq(), book); diff --git a/src/xrpld/rpc/handlers/Simulate.cpp b/src/xrpld/rpc/handlers/transaction/Simulate.cpp similarity index 97% rename from src/xrpld/rpc/handlers/Simulate.cpp rename to src/xrpld/rpc/handlers/transaction/Simulate.cpp index 1d46f72425..433603338d 100644 --- a/src/xrpld/rpc/handlers/Simulate.cpp +++ b/src/xrpld/rpc/handlers/transaction/Simulate.cpp @@ -39,10 +39,10 @@ getAutofillSequence(Json::Value const& tx_json, RPC::JsonContext& context) RPC::make_error(rpcSRC_ACT_MALFORMED, RPC::invalid_field_message("tx.Account"))); } std::shared_ptr const sle = - context.app.openLedger().current()->read(keylet::account(*srcAddressID)); + context.app.getOpenLedger().current()->read(keylet::account(*srcAddressID)); if (!hasTicketSeq && !sle) { - JLOG(context.app.journal("Simulate").debug()) + JLOG(context.app.getJournal("Simulate").debug()) << "Failed to find source account " << "in current ledger: " << toBase58(*srcAddressID); @@ -210,7 +210,7 @@ simulateTxn(RPC::JsonContext& context, std::shared_ptr transaction) { Json::Value jvResult; // Process the transaction - OpenView view = *context.app.openLedger().current(); + OpenView view = *context.app.getOpenLedger().current(); auto const result = context.app.getTxQ().apply( context.app, view, transaction->getSTransaction(), tapDRY_RUN, context.j); @@ -339,7 +339,7 @@ doSimulate(RPC::JsonContext& context) { return simulateTxn(context, transaction); } - // LCOV_EXCL_START this is just in case, so rippled doesn't crash + // LCOV_EXCL_START this is just in case, so xrpld doesn't crash catch (std::exception const& e) { Json::Value jvResult = Json::objectValue; diff --git a/src/xrpld/rpc/handlers/Submit.cpp b/src/xrpld/rpc/handlers/transaction/Submit.cpp similarity index 99% rename from src/xrpld/rpc/handlers/Submit.cpp rename to src/xrpld/rpc/handlers/transaction/Submit.cpp index b8cca3670a..cac7259a00 100644 --- a/src/xrpld/rpc/handlers/Submit.cpp +++ b/src/xrpld/rpc/handlers/transaction/Submit.cpp @@ -55,7 +55,7 @@ doSubmit(RPC::JsonContext& context) auto ret = strUnHex(context.params[jss::tx_blob].asString()); - if (!ret || !ret->size()) + if (!ret || ret->empty()) return rpcError(rpcINVALID_PARAMS); SerialIter sitTrans(makeSlice(*ret)); @@ -76,8 +76,10 @@ doSubmit(RPC::JsonContext& context) { if (!context.app.checkSigs()) + { forceValidity( context.app.getHashRouter(), stTx->getTransactionID(), Validity::SigGoodOnly); + } auto [validity, reason] = checkValidity( context.app.getHashRouter(), *stTx, context.ledgerMaster.getCurrentLedger()->rules()); if (validity != Validity::Valid) diff --git a/src/xrpld/rpc/handlers/SubmitMultiSigned.cpp b/src/xrpld/rpc/handlers/transaction/SubmitMultiSigned.cpp similarity index 100% rename from src/xrpld/rpc/handlers/SubmitMultiSigned.cpp rename to src/xrpld/rpc/handlers/transaction/SubmitMultiSigned.cpp diff --git a/src/xrpld/rpc/handlers/TransactionEntry.cpp b/src/xrpld/rpc/handlers/transaction/TransactionEntry.cpp similarity index 97% rename from src/xrpld/rpc/handlers/TransactionEntry.cpp rename to src/xrpld/rpc/handlers/transaction/TransactionEntry.cpp index 8cd1120aad..36f53130fa 100644 --- a/src/xrpld/rpc/handlers/TransactionEntry.cpp +++ b/src/xrpld/rpc/handlers/transaction/TransactionEntry.cpp @@ -59,8 +59,10 @@ doTransactionEntry(RPC::JsonContext& context) jvResult[jss::hash] = to_string(sttx->getTransactionID()); if (!lpLedger->open()) + { jvResult[jss::ledger_hash] = to_string(context.ledgerMaster.getHashBySeq(lpLedger->seq())); + } bool const validated = context.ledgerMaster.isValidated(*lpLedger); @@ -73,7 +75,9 @@ doTransactionEntry(RPC::JsonContext& context) } } else + { jvResult[jss::tx_json] = sttx->getJson(JsonOptions::none); + } RPC::insertDeliverMax(jvResult[jss::tx_json], sttx->getTxnType(), context.apiVersion); diff --git a/src/xrpld/rpc/handlers/Tx.cpp b/src/xrpld/rpc/handlers/transaction/Tx.cpp similarity index 92% rename from src/xrpld/rpc/handlers/Tx.cpp rename to src/xrpld/rpc/handlers/transaction/Tx.cpp index 84f2a6c618..a3ed788060 100644 --- a/src/xrpld/rpc/handlers/Tx.cpp +++ b/src/xrpld/rpc/handlers/transaction/Tx.cpp @@ -42,7 +42,7 @@ struct TxResult std::optional ctid; std::optional closeTime; std::optional ledgerHash; - TxSearched searchedAll; + TxSearched searchedAll = TxSearched::Unknown; }; struct TxArgs @@ -77,7 +77,7 @@ doTxHelp(RPC::Context& context, TxArgs args) using TxPair = std::pair, std::shared_ptr>; - result.searchedAll = TxSearched::unknown; + result.searchedAll = TxSearched::Unknown; std::variant v; if (args.ctid) @@ -125,7 +125,8 @@ doTxHelp(RPC::Context& context, TxArgs args) return {result, rpcSUCCESS}; } - std::shared_ptr ledger = context.ledgerMaster.getLedgerBySeq(txn->getLedger()); + std::shared_ptr const ledger = + context.ledgerMaster.getLedgerBySeq(txn->getLedger()); if (ledger && !ledger->open()) result.ledgerHash = ledger->header().hash; @@ -148,12 +149,12 @@ doTxHelp(RPC::Context& context, TxArgs args) // compute outgoing CTID if (meta->getAsObject().isFieldPresent(sfTransactionIndex)) { - uint32_t lgrSeq = ledger->header().seq; - uint32_t txnIdx = meta->getAsObject().getFieldU32(sfTransactionIndex); - uint32_t netID = context.app.getNetworkIDService().getNetworkID(); + uint32_t const lgrSeq = ledger->header().seq; + uint32_t const txnIdx = meta->getAsObject().getFieldU32(sfTransactionIndex); + uint32_t const netID = context.app.getNetworkIDService().getNetworkID(); if (txnIdx <= 0xFFFFU && netID < 0xFFFFU && lgrSeq < 0x0FFF'FFFFUL) - result.ctid = RPC::encodeCTID(lgrSeq, (uint32_t)txnIdx, (uint32_t)netID); + result.ctid = RPC::encodeCTID(lgrSeq, txnIdx, netID); } } @@ -172,10 +173,10 @@ populateJsonResponse( // handle errors if (error.toErrorCode() != rpcSUCCESS) { - if (error.toErrorCode() == rpcTXN_NOT_FOUND && result.searchedAll != TxSearched::unknown) + if (error.toErrorCode() == rpcTXN_NOT_FOUND && result.searchedAll != TxSearched::Unknown) { response = Json::Value(Json::objectValue); - response[jss::searched_all] = (result.searchedAll == TxSearched::all); + response[jss::searched_all] = (result.searchedAll == TxSearched::All); error.inject(response); } else @@ -192,7 +193,9 @@ populateJsonResponse( constexpr auto optionsJson = JsonOptions::include_date | JsonOptions::disable_API_prior_V2; if (args.binary) + { response[jss::tx_blob] = result.txn->getJson(optionsJson, true); + } else { response[jss::tx_json] = result.txn->getJson(optionsJson); @@ -258,8 +261,10 @@ doTxJson(RPC::JsonContext& context) TxArgs args; if (context.params.isMember(jss::transaction) && context.params.isMember(jss::ctid)) + { // specifying both is ambiguous return rpcError(rpcINVALID_PARAMS); + } if (context.params.isMember(jss::transaction)) { @@ -286,7 +291,9 @@ doTxJson(RPC::JsonContext& context) args.ctid = {lgr_seq, txn_idx}; } else + { return rpcError(rpcINVALID_PARAMS); + } args.binary = context.params.isMember(jss::binary) && context.params[jss::binary].asBool(); @@ -304,7 +311,7 @@ doTxJson(RPC::JsonContext& context) } } - std::pair res = doTxHelp(context, args); + std::pair const res = doTxHelp(context, args); return populateJsonResponse(res, args, context); } diff --git a/src/xrpld/rpc/handlers/TxHistory.cpp b/src/xrpld/rpc/handlers/transaction/TxHistory.cpp similarity index 94% rename from src/xrpld/rpc/handlers/TxHistory.cpp rename to src/xrpld/rpc/handlers/transaction/TxHistory.cpp index 02ff6fb43c..3467b1c990 100644 --- a/src/xrpld/rpc/handlers/TxHistory.cpp +++ b/src/xrpld/rpc/handlers/transaction/TxHistory.cpp @@ -27,7 +27,7 @@ doTxHistory(RPC::JsonContext& context) if (!context.params.isMember(jss::start)) return rpcError(rpcINVALID_PARAMS); - unsigned int startIndex = context.params[jss::start].asUInt(); + unsigned int const startIndex = context.params[jss::start].asUInt(); if ((startIndex > 10000) && (!isUnlimited(context.role))) return rpcError(rpcNO_PERMISSION); diff --git a/src/xrpld/rpc/handlers/TxReduceRelay.cpp b/src/xrpld/rpc/handlers/transaction/TxReduceRelay.cpp similarity index 83% rename from src/xrpld/rpc/handlers/TxReduceRelay.cpp rename to src/xrpld/rpc/handlers/transaction/TxReduceRelay.cpp index d269ef448e..f24f0862ef 100644 --- a/src/xrpld/rpc/handlers/TxReduceRelay.cpp +++ b/src/xrpld/rpc/handlers/transaction/TxReduceRelay.cpp @@ -9,7 +9,7 @@ namespace xrpl { Json::Value doTxReduceRelay(RPC::JsonContext& context) { - return context.app.overlay().txMetrics(); + return context.app.getOverlay().txMetrics(); } } // namespace xrpl diff --git a/src/xrpld/rpc/handlers/Ping.cpp b/src/xrpld/rpc/handlers/utility/Ping.cpp similarity index 82% rename from src/xrpld/rpc/handlers/Ping.cpp rename to src/xrpld/rpc/handlers/utility/Ping.cpp index cc3c558cd9..695d90b964 100644 --- a/src/xrpld/rpc/handlers/Ping.cpp +++ b/src/xrpld/rpc/handlers/utility/Ping.cpp @@ -22,12 +22,14 @@ doPing(RPC::JsonContext& context) case Role::IDENTIFIED: ret[jss::role] = "identified"; ret[jss::username] = std::string{context.headers.user}; - if (context.headers.forwardedFor.size()) + if (!context.headers.forwardedFor.empty()) ret[jss::ip] = std::string{context.headers.forwardedFor}; break; case Role::PROXY: ret[jss::role] = "proxied"; - ret[jss::ip] = std::string{context.headers.forwardedFor}; + if (!context.headers.forwardedFor.empty()) + ret[jss::ip] = std::string{context.headers.forwardedFor}; + break; default:; } diff --git a/src/xrpld/rpc/handlers/Random.cpp b/src/xrpld/rpc/handlers/utility/Random.cpp similarity index 97% rename from src/xrpld/rpc/handlers/Random.cpp rename to src/xrpld/rpc/handlers/utility/Random.cpp index 2fb8abf3c9..5ed4426940 100644 --- a/src/xrpld/rpc/handlers/Random.cpp +++ b/src/xrpld/rpc/handlers/utility/Random.cpp @@ -10,7 +10,7 @@ namespace xrpl { namespace RPC { struct JsonContext; -} +} // namespace RPC // Result: // { diff --git a/src/xrpld/shamap/NodeFamily.cpp b/src/xrpld/shamap/NodeFamily.cpp index a0821ea202..3460c68608 100644 --- a/src/xrpld/shamap/NodeFamily.cpp +++ b/src/xrpld/shamap/NodeFamily.cpp @@ -9,12 +9,12 @@ namespace xrpl { NodeFamily::NodeFamily(Application& app, CollectorManager& cm) : app_(app) , db_(app.getNodeStore()) - , j_(app.journal("NodeFamily")) + , j_(app.getJournal("NodeFamily")) , fbCache_( std::make_shared( "Node family full below cache", stopwatch(), - app.journal("NodeFamilyFulLBelowCache"), + app.getJournal("NodeFamilyFulLBelowCache"), cm.collector(), fullBelowTargetSize, fullBelowExpiration)) @@ -39,7 +39,7 @@ void NodeFamily::reset() { { - std::lock_guard lock(maxSeqMutex_); + std::lock_guard const lock(maxSeqMutex_); maxSeq_ = 0; } diff --git a/tests/README.md b/tests/README.md index c4a96005e7..431c2c464a 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,5 +1,5 @@ # Integration tests This directory contains integration tests for the project. These tests are run -against the `libxrpl` library or `rippled` binary to verify they are working as +against the `libxrpl` library or `xrpld` binary to verify they are working as expected.