From e9d885bd9b8d7b6f0fb0fbcd3db7ff39df34e68b Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Tue, 26 May 2026 14:50:18 +0100 Subject: [PATCH 01/28] fix: Fix clang-tidy pre-commit hook to locate compile_commands.json from repo root (#7325) Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> --- .github/scripts/levelization/generate.py | 0 bin/pre-commit/clang_tidy_check.py | 8 +++++++- 2 files changed, 7 insertions(+), 1 deletion(-) mode change 100644 => 100755 .github/scripts/levelization/generate.py diff --git a/.github/scripts/levelization/generate.py b/.github/scripts/levelization/generate.py old mode 100644 new mode 100755 diff --git a/bin/pre-commit/clang_tidy_check.py b/bin/pre-commit/clang_tidy_check.py index 7fb51d1c46..f134660671 100755 --- a/bin/pre-commit/clang_tidy_check.py +++ b/bin/pre-commit/clang_tidy_check.py @@ -168,7 +168,13 @@ def main(): if not os.environ.get("TIDY"): return 0 - repo_root = Path(__file__).parent.parent + repo_root = Path( + subprocess.check_output( + ["git", "rev-parse", "--show-toplevel"], + cwd=Path(__file__).parent, + text=True, + ).strip() + ) files = staged_files(repo_root) if not files: return 0 From 22a21b175efbf95a3435360ba5d6a85ffbfcbcd2 Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Tue, 26 May 2026 16:01:52 +0200 Subject: [PATCH 02/28] fix: Include management-fee delta in doOverpayment assertion (#7039) --- src/libxrpl/ledger/helpers/LendingHelpers.cpp | 117 ++++++++++------- src/test/app/Loan_test.cpp | 118 ++++++++++++++---- 2 files changed, 168 insertions(+), 67 deletions(-) diff --git a/src/libxrpl/ledger/helpers/LendingHelpers.cpp b/src/libxrpl/ledger/helpers/LendingHelpers.cpp index 1fedbb5f13..9cda5905c9 100644 --- a/src/libxrpl/ledger/helpers/LendingHelpers.cpp +++ b/src/libxrpl/ledger/helpers/LendingHelpers.cpp @@ -769,9 +769,6 @@ doOverpayment( "xrpl::detail::doOverpayment", "principal change agrees"); - // I'm not 100% sure the following asserts are correct. If in doubt, and - // everything else works, remove any that cause trouble. - JLOG(j.debug()) << "valueChange: " << loanPaymentParts.valueChange << ", totalValue before: " << *totalValueOutstandingProxy << ", totalValue after: " << newRoundedLoanState.valueOutstanding @@ -783,11 +780,28 @@ doOverpayment( << overpaymentComponents.trackedPrincipalDelta - (totalValueOutstandingProxy - newRoundedLoanState.valueOutstanding); + // The valueChange returned by tryOverpayment satisfies + // valueChange = (newInterestDue - oldInterestDue) + untrackedInterest. + // Using the loan-state identity v = p + i + m and the adjacent + // `principal change agrees` assertion (dp = oldP - newP), this + // rearranges into three independently-computable terms: + // + // 1. TVO change beyond what principal repayment alone explains: + // newTVO - (oldTVO - dp) + // 2. Management fee released by re-amortization (positive when + // mfee decreased; zero when managementFeeRate == 0): + // oldMfee - newMfee + // 3. The overpayment's penalty interest part (= untrackedInterest + // for the overpayment path; see computeOverpaymentComponents): + // trackedInterestPart() + [[maybe_unused]] Number const tvoChange = newRoundedLoanState.valueOutstanding - + (totalValueOutstandingProxy - overpaymentComponents.trackedPrincipalDelta); + [[maybe_unused]] Number const managementFeeReleased = + managementFeeOutstandingProxy - newRoundedLoanState.managementFeeDue; + [[maybe_unused]] Number const interestPart = overpaymentComponents.trackedInterestPart(); + XRPL_ASSERT_PARTS( - loanPaymentParts.valueChange == - newRoundedLoanState.valueOutstanding - - (totalValueOutstandingProxy - overpaymentComponents.trackedPrincipalDelta) + - overpaymentComponents.trackedInterestPart(), + loanPaymentParts.valueChange == tvoChange + managementFeeReleased + interestPart, "xrpl::detail::doOverpayment", "interest paid agrees"); @@ -2027,51 +2041,62 @@ loanMakePayment( // It shouldn't be possible for the overpayment to be greater than // totalValueOutstanding, because that would have been processed as // another normal payment. But cap it just in case. - Number const overpayment = std::min(roundedAmount - totalPaid, *totalValueOutstandingProxy); + Number const overpaymentRaw = + std::min(roundedAmount - totalPaid, *totalValueOutstandingProxy); - detail::ExtendedPaymentComponents const overpaymentComponents = - detail::computeOverpaymentComponents( - asset, - loanScale, - overpayment, - overpaymentInterestRate, - overpaymentFeeRate, - managementFeeRate); + bool const fixEnabled = view.rules().enabled(fixCleanup3_2_0); + Number const overpayment = fixEnabled + ? roundToAsset(asset, overpaymentRaw, loanScale, Number::RoundingMode::Downward) + : overpaymentRaw; - // Don't process an overpayment if the whole amount (or more!) - // gets eaten by fees and interest. - if (overpaymentComponents.trackedPrincipalDelta > 0) + // Post-amendment, the rounded overpayment can be zero; pre-amendment + // it's always positive given the surrounding guards. + if (!fixEnabled || overpayment > 0) { - XRPL_ASSERT_PARTS( - overpaymentComponents.untrackedInterest >= beast::kZero, - "xrpl::loanMakePayment", - "overpayment penalty did not reduce value of loan"); - // Can't just use `periodicPayment` here, because it might - // change - auto periodicPaymentProxy = loan->at(sfPeriodicPayment); - if (auto const overResult = detail::doOverpayment( - view.rules(), + detail::ExtendedPaymentComponents const overpaymentComponents = + detail::computeOverpaymentComponents( asset, loanScale, - overpaymentComponents, - totalValueOutstandingProxy, - principalOutstandingProxy, - managementFeeOutstandingProxy, - periodicPaymentProxy, - periodicRate, - paymentRemainingProxy, - managementFeeRate, - j)) + overpayment, + overpaymentInterestRate, + overpaymentFeeRate, + managementFeeRate); + + // Don't process an overpayment if the whole amount (or more!) + // gets eaten by fees and interest. + if (overpaymentComponents.trackedPrincipalDelta > 0) { - totalParts += *overResult; - } - else if (overResult.error()) - { - // error() will be the TER returned if a payment is not - // made. It will only evaluate to true if it's unsuccessful. - // Otherwise, tesSUCCESS means nothing was done, so - // continue. - return Unexpected(overResult.error()); + XRPL_ASSERT_PARTS( + overpaymentComponents.untrackedInterest >= beast::kZero, + "xrpl::loanMakePayment", + "overpayment penalty did not reduce value of loan"); + // Can't just use `periodicPayment` here, because it might + // change + auto periodicPaymentProxy = loan->at(sfPeriodicPayment); + if (auto const overResult = detail::doOverpayment( + view.rules(), + asset, + loanScale, + overpaymentComponents, + totalValueOutstandingProxy, + principalOutstandingProxy, + managementFeeOutstandingProxy, + periodicPaymentProxy, + periodicRate, + paymentRemainingProxy, + managementFeeRate, + j)) + { + totalParts += *overResult; + } + else if (overResult.error()) + { + // error() will be the TER returned if a payment is not + // made. It will only evaluate to true if it's unsuccessful. + // Otherwise, tesSUCCESS means nothing was done, so + // continue. + return Unexpected(overResult.error()); + } } } } diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 5e8e89cefa..c380655563 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -169,6 +169,10 @@ protected: TenthBips32 coverRateLiquidation = percentageToTenthBips(25); std::string data = {}; // NOLINT(readability-redundant-member-init) std::uint32_t flags = 0; + // If set, the vault is created with this sfScale value. Useful for + // tests that need finer loanScale to exercise rounding edge cases. + std::optional vaultScale = + std::nullopt; // NOLINT(readability-redundant-member-init) [[nodiscard]] Number maxCoveredLoanValue(Number const& currentDebt) const @@ -522,6 +526,8 @@ protected: auto const coverRateMinValue = params.coverRateMin; auto [tx, vaultKeylet] = vault.create({.owner = lender, .asset = asset}); + if (params.vaultScale) + tx[sfScale] = *params.vaultScale; env(tx); env.close(); BEAST_EXPECT(env.le(vaultKeylet)); @@ -2157,21 +2163,23 @@ protected: // If the loan does not allow overpayments, send a payment that // tries to make an overpayment. Do not include `txFlags`, so we // don't end up duplicating the next test transaction. - env(pay(borrower, - loanKeylet.key, - STAmount{broker.asset, state.periodicPayment * Number{15, -1}}, - tfLoanOverpayment), - Fee(XRPAmount{baseFee * (Number{15, -1} / kLoanPaymentsPerFeeIncrement + 1)}), - Ter(tecNO_PERMISSION)); + // + // fixCleanup3_1_3 gates tfLoanOverpayment as a valid flag: + // with fix on → preflight passes, apply returns tecNO_PERMISSION; + // with fix off → preflight rejects the flag, returns temINVALID_FLAG. + bool const hasFix313 = env.current()->rules().enabled(fixCleanup3_1_3); + STAmount const overpayAmount{broker.asset, state.periodicPayment * Number{15, -1}}; + XRPAmount const overpayFee{ + baseFee * (Number{15, -1} / kLoanPaymentsPerFeeIncrement + 1)}; + env(pay(borrower, loanKeylet.key, overpayAmount, tfLoanOverpayment), + Fee(overpayFee), + Ter(hasFix313 ? TER{tecNO_PERMISSION} : TER{temINVALID_FLAG})); + if (hasFix313) { env.disableFeature(fixCleanup3_1_3); - env(pay(borrower, - loanKeylet.key, - STAmount{broker.asset, state.periodicPayment * Number{15, -1}}, - tfLoanOverpayment), - Fee(XRPAmount{ - baseFee * (Number{15, -1} / kLoanPaymentsPerFeeIncrement + 1)}), + env(pay(borrower, loanKeylet.key, overpayAmount, tfLoanOverpayment), + Fee(overpayFee), Ter(temINVALID_FLAG)); env.enableFeature(fixCleanup3_1_3); } @@ -7027,7 +7035,7 @@ protected: auto credType = "credential1"; - pdomain::Credentials const credentials1{{.issuer = issuer, .credType = credType}}; + pdomain::Credentials const credentials1 = {{.issuer = issuer, .credType = credType}}; env(pdomain::setTx(issuer, credentials1)); env.close(); @@ -7572,6 +7580,74 @@ protected: attemptWithdrawShares(depositorB, sharesLpB, tesSUCCESS); } + // An overpayment whose residual amount has more precision than loanScale + // fires the isRounded(asset, overpayment, loanScale) assertion in + // computeOverpaymentComponents (and a downstream "interest paid agrees" + // assertion in doOverpayment). fixCleanup3_2_0 rounds the residual down + // to loanScale before passing it in. The pre-amendment path can't be + // tested here because the assertion fires in Debug builds and aborts + // the test process — see the PR description for context. + void + testBugOverpayUnroundedAmount() + { + testcase("bug: computeOverpaymentComponents isRounded assertion"); + + using namespace jtx; + using namespace loan; + Env env(*this, all_); + + Account const issuer{"issuer"}; + Account const lender{"vaultOwner"}; + Account const borrower{"borrower"}; + + env.fund(XRP(1'000'000), issuer, lender, borrower); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const iouAsset = issuer["USD"]; + STAmount const iouLimit{iouAsset.raw(), Number{9'999'999'999'999'999LL}}; + env(trust(lender, iouLimit)); + env(trust(borrower, iouLimit)); + env(pay(issuer, lender, iouAsset(1'000'000))); + env(pay(issuer, borrower, iouAsset(1'000'000))); + env.close(); + + auto const broker = createVaultAndBroker( + env, + iouAsset, + lender, + {.vaultDeposit = 100'000, + .debtMax = 5000, + .managementFeeRate = TenthBips16{1000}, + .vaultScale = 1}); + + auto const sleBroker = env.le(broker.brokerKeylet()); + if (!BEAST_EXPECT(sleBroker)) + return; + auto const loanSequence = sleBroker->at(sfLoanSequence); + auto const loanKeylet = keylet::loan(broker.brokerID, loanSequence); + + using namespace loan; + env(set(borrower, broker.brokerID, Number{1000}, tfLoanOverpayment), + Sig(sfCounterpartySignature, lender), + kInterestRate(TenthBips32{10000}), + kPaymentTotal(12), + kPaymentInterval(60), + kGracePeriod(60), + kOverpaymentFee(TenthBips32{1000}), + kOverpaymentInterestRate(TenthBips32{1000}), + Fee(env.current()->fees().base * 2), + Ter(tesSUCCESS)); + env.close(); + + // periodic * 1.5 at 15-sig-digit precision: 125.000154585042. This + // has too many digits to round cleanly to loanScale=-10, so the + // overpayment residual fails the isRounded check. + STAmount const payAmount{iouAsset.raw(), Number{125'000'154'585'042LL, -12}}; + env(pay(borrower, loanKeylet.key, payAmount), Txflags(tfLoanOverpayment), Ter(tesSUCCESS)); + env.close(); + } + // Regression for the dual-rounding fix at coarse (integer-MPT) scale. // // Loan: P=1, r=50% (50000 tenth-bips), n=3, yearly interval. The @@ -8280,6 +8356,9 @@ protected: testRIPD3901(); testBorrowerIsBroker(); testLimitExceeded(); + testLoanSetBlockedLoanPayAllowedWhenCanTransferCleared(); + testLendingCanTradeClearedNoImpact(); + testBugOverpayUnroundedAmount(); for (auto const flags : {0u, tfLoanOverpayment}) testYieldTheftRounding(flags); @@ -8295,11 +8374,11 @@ protected: testLoanPayLateFullPaymentBypassesPenalties(features); testLoanCoverMinimumRoundingExploit(features); #endif - // Lifecycle - testSelfLoan(features); - testLoanSet(features); testLifecycle(features); + testLoanSet(features); + testDosLoanPay(features); + testSelfLoan(features); // Payment paths testWithdrawReflectsUnrealizedLoss(features); @@ -8346,11 +8425,8 @@ public: run() override { runAmendmentIndependent(); - testLoanSetBlockedLoanPayAllowedWhenCanTransferCleared(); - testLendingCanTradeClearedNoImpact(); - testDosLoanPay(all_ | fixCleanup3_1_3); - testDosLoanPay(all_ - fixCleanup3_1_3); - for (auto const& features : amendmentCombinations({fixCleanup3_2_0, featureMPTokensV2})) + for (auto const& features : + amendmentCombinations({fixCleanup3_1_3, fixCleanup3_2_0, featureMPTokensV2})) runAmendmentSensitive(features); } }; From 49cb3f45a42d1f050212e1653a663789fb592d2a Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 26 May 2026 16:45:33 +0100 Subject: [PATCH 03/28] ci: Add clang to nix images (#7308) Co-authored-by: semgrep-companion-app[bot] <218312740+semgrep-companion-app[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/build-nix-image.yml | 114 ++++++++++-------- .../workflows/reusable-build-docker-image.yml | 89 ++++++++++++++ cspell.config.yaml | 3 + docker/check-sanitizers.sh | 42 +++++++ docker/cpp_files/asan.cpp | 28 +++++ docker/cpp_files/tsan.cpp | 26 ++++ docker/cpp_files/ubsan.cpp | 13 ++ docker/nix.Dockerfile | 29 +++++ flake.lock | 4 +- flake.nix | 6 +- nix/ci-env.nix | 108 +++++++++++++---- nix/packages.nix | 1 + nix/utils.nix | 8 +- 13 files changed, 387 insertions(+), 84 deletions(-) create mode 100644 .github/workflows/reusable-build-docker-image.yml create mode 100755 docker/check-sanitizers.sh create mode 100644 docker/cpp_files/asan.cpp create mode 100644 docker/cpp_files/tsan.cpp create mode 100644 docker/cpp_files/ubsan.cpp diff --git a/.github/workflows/build-nix-image.yml b/.github/workflows/build-nix-image.yml index 6554cf6c08..a8fb6eec86 100644 --- a/.github/workflows/build-nix-image.yml +++ b/.github/workflows/build-nix-image.yml @@ -6,14 +6,16 @@ on: - develop paths: - ".github/workflows/build-nix-image.yml" - - "docker/nix.Dockerfile" + - ".github/workflows/reusable-build-docker-image.yml" + - "docker/**" - "flake.nix" - "flake.lock" - "nix/**" pull_request: paths: - ".github/workflows/build-nix-image.yml" - - "docker/nix.Dockerfile" + - ".github/workflows/reusable-build-docker-image.yml" + - "docker/**" - "flake.nix" - "flake.lock" - "nix/**" @@ -27,75 +29,81 @@ defaults: run: shell: bash -env: - UBUNTU_VERSION: "20.04" - RHEL_VERSION: "9" - DEBIAN_VERSION: "bookworm" - jobs: build: - name: Build and push Nix image (${{ matrix.distro }}) + name: Build ${{ matrix.distro.name }} (${{ matrix.target.platform }}) + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + # The base images are the oldest supported version of each distro + # that we want to build images for. + distro: + - name: nixos + base_image: nixos/nix:latest + - name: ubuntu + base_image: ubuntu:20.04 + - name: rhel + base_image: registry.access.redhat.com/ubi9/ubi:latest + - name: debian + base_image: debian:bookworm + target: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-24.04-arm + uses: ./.github/workflows/reusable-build-docker-image.yml + with: + image_name: ghcr.io/xrplf/xrpld/nix-${{ matrix.distro.name }} + dockerfile: docker/nix.Dockerfile + base_image: ${{ matrix.distro.base_image }} + platform: ${{ matrix.target.platform }} + runner: ${{ matrix.target.runner }} + push: ${{ github.event_name == 'push' }} + + merge: + name: Merge ${{ matrix.distro }} manifest + needs: build + if: github.event_name == 'push' runs-on: ubuntu-latest permissions: contents: read packages: write strategy: + fail-fast: false matrix: - include: - - distro: nixos - - distro: ubuntu - - distro: rhel - - distro: debian + distro: [nixos, ubuntu, rhel, debian] + env: + IMAGE_NAME: ghcr.io/xrplf/xrpld/nix-${{ matrix.distro }} steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Determine base image - id: vars - run: | - case "${{ matrix.distro }}" in - nixos) - echo "base_image=nixos/nix:latest" >> $GITHUB_OUTPUT - ;; - ubuntu) - echo "base_image=ubuntu:${UBUNTU_VERSION}" >> $GITHUB_OUTPUT - ;; - rhel) - echo "base_image=registry.access.redhat.com/ubi${RHEL_VERSION}/ubi:latest" >> $GITHUB_OUTPUT - ;; - debian) - echo "base_image=debian:${DEBIAN_VERSION}" >> $GITHUB_OUTPUT - ;; - esac - - name: Set up Docker Buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + - name: Docker metadata + id: meta + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 + with: + images: ${{ env.IMAGE_NAME }} + tags: | + type=sha,prefix=sha-,format=short + type=raw,value=latest + - name: Login to GitHub Container Registry - if: github.event_name == 'push' uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Docker metadata - id: meta - uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 - with: - images: ghcr.io/xrplf/ci/nix-${{ matrix.distro }} - tags: | - type=sha,prefix=sha-,format=short - type=raw,value=latest + - name: Create multi-arch manifests + run: | + for tag in $(jq -cr '.tags[]' <<< "$DOCKER_METADATA_OUTPUT_JSON"); do + docker buildx imagetools create -t "$tag" "${tag}-amd64" "${tag}-arm64" + done - - name: Build and push - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 - with: - context: . - file: docker/nix.Dockerfile - platforms: linux/amd64 - push: ${{ github.event_name == 'push' }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - build-args: BASE_IMAGE=${{ steps.vars.outputs.base_image }} + - name: Inspect image + run: | + docker buildx imagetools inspect "${IMAGE_NAME}:${{ steps.meta.outputs.version }}" diff --git a/.github/workflows/reusable-build-docker-image.yml b/.github/workflows/reusable-build-docker-image.yml new file mode 100644 index 0000000000..e631e02368 --- /dev/null +++ b/.github/workflows/reusable-build-docker-image.yml @@ -0,0 +1,89 @@ +# Build a single-platform Docker image. On push, the image is pushed to +# GHCR with arch-suffixed tags (e.g. `:latest-amd64`, `:sha-abc-amd64`) +# so the calling workflow can stitch per-arch builds into a multi-arch +# manifest without needing to pass digests around. +name: Reusable build Docker image (single platform) + +on: + workflow_call: + inputs: + image_name: + description: "Full image name without tag (e.g. 'ghcr.io/xrplf/xrpld/nix-ubuntu')" + required: true + type: string + dockerfile: + description: "Path to the Dockerfile, relative to the repository root" + required: true + type: string + base_image: + description: "Value passed to the Dockerfile as the BASE_IMAGE build arg" + required: true + type: string + platform: + description: "Docker platform string, e.g. linux/amd64" + required: true + type: string + runner: + description: "GitHub Actions runner label to build on" + required: true + type: string + push: + description: "Whether to push the image to GHCR" + required: true + type: boolean + +defaults: + run: + shell: bash + +jobs: + build: + name: Build (${{ inputs.platform }}) + runs-on: ${{ inputs.runner }} + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Determine arch + id: vars + env: + PLATFORM: ${{ inputs.platform }} + run: | + echo "arch=${PLATFORM##*/}" >> $GITHUB_OUTPUT + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + + - name: Login to GitHub Container Registry + if: inputs.push + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Docker metadata + id: meta + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 + with: + images: ${{ inputs.image_name }} + tags: | + type=sha,prefix=sha-,format=short + type=raw,value=latest + flavor: | + suffix=-${{ steps.vars.outputs.arch }},onlatest=true + + - name: Build and push + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + with: + context: . + file: ${{ inputs.dockerfile }} + platforms: ${{ inputs.platform }} + push: ${{ inputs.push }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: BASE_IMAGE=${{ inputs.base_image }} diff --git a/cspell.config.yaml b/cspell.config.yaml index 275df41f58..82a6a20158 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -199,11 +199,13 @@ words: - nonxrp - noreplace - noripple + - nostdinc - notifempty - nudb - nullptr - nunl - Nyffenegger + - onlatest - ostr - pargs - partitioner @@ -298,6 +300,7 @@ words: - unauthorizing - unergonomic - unfetched + - unfindable - unflatten - unfund - unimpair diff --git a/docker/check-sanitizers.sh b/docker/check-sanitizers.sh new file mode 100755 index 0000000000..db51f11851 --- /dev/null +++ b/docker/check-sanitizers.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# Sanity-check that the sanitizer runtimes shipped with g++/clang++ work +# end-to-end against the system loader: compile each example with both +# compilers, run it, and confirm the expected diagnostic is emitted. + +set -eo pipefail + +cpp_files_dir="${1:?usage: $0 }" + +case "$(uname -m)" in + x86_64) loader=/lib64/ld-linux-x86-64.so.2 ;; + aarch64) loader=/lib/ld-linux-aarch64.so.1 ;; + *) echo "Unsupported arch: $(uname -m)" >&2; exit 1 ;; +esac + +declare -A sanitize=( + [asan]="-fsanitize=address" + [tsan]="-fsanitize=thread" + [ubsan]="-fsanitize=undefined" +) +declare -A expect=( + [asan]="heap-use-after-free" + [tsan]="data race" + [ubsan]="signed integer overflow" +) + +for compiler in g++ clang++; do + for name in asan tsan ubsan; do + bin="/tmp/${name}-${compiler}" + echo "=== Build ${name} with ${compiler} ===" + "$compiler" -std=c++20 -O1 -g ${sanitize[$name]} \ + -Wl,--dynamic-linker=$loader \ + "${cpp_files_dir}/${name}.cpp" -o "$bin" + echo "=== Run ${name}-${compiler} ===" + output=$("$bin" 2>&1) || true + echo "$output" + echo "$output" | grep -q "${expect[$name]}" \ + || { echo "expected '${expect[$name]}' from $bin"; exit 1; } + rm -f "$bin" + done +done diff --git a/docker/cpp_files/asan.cpp b/docker/cpp_files/asan.cpp new file mode 100644 index 0000000000..8347f58d37 --- /dev/null +++ b/docker/cpp_files/asan.cpp @@ -0,0 +1,28 @@ +#include +#include +#include + +#if defined(__clang__) || defined(__GNUC__) +__attribute__((noinline)) +#elif defined(_MSC_VER) +__declspec(noinline) +#endif +int +read_after_free(volatile int* array, std::size_t index) +{ + std::atomic_signal_fence(std::memory_order_seq_cst); + int value = array[index]; + std::atomic_signal_fence(std::memory_order_seq_cst); + return value; +} + +int +main() +{ + int* array = new int[5]{10, 20, 30, 40, 50}; + delete[] array; + + std::cout << "Value at index 2: " << read_after_free(array, 2) << std::endl; + + return 0; +} diff --git a/docker/cpp_files/tsan.cpp b/docker/cpp_files/tsan.cpp new file mode 100644 index 0000000000..34b0990a6d --- /dev/null +++ b/docker/cpp_files/tsan.cpp @@ -0,0 +1,26 @@ +#include +#include + +static int kCounter = 0; + +void +increment() +{ + for (int i = 0; i < 100'000; ++i) + { + ++kCounter; + } +} + +int +main() +{ + std::thread t1(increment); + std::thread t2(increment); + + t1.join(); + t2.join(); + + std::cout << "Final counter value: " << kCounter << std::endl; + return 0; +} diff --git a/docker/cpp_files/ubsan.cpp b/docker/cpp_files/ubsan.cpp new file mode 100644 index 0000000000..db86119070 --- /dev/null +++ b/docker/cpp_files/ubsan.cpp @@ -0,0 +1,13 @@ +#include +#include + +int +main() +{ + int maxInt = std::numeric_limits::max(); + int volatile one = 1; + std::cout << "Current max: " << maxInt << std::endl; + int overflowed = maxInt + one; + std::cout << "Overflowed result: " << overflowed << std::endl; + return 0; +} diff --git a/docker/nix.Dockerfile b/docker/nix.Dockerfile index 52faa8b8dc..690f0b76bd 100644 --- a/docker/nix.Dockerfile +++ b/docker/nix.Dockerfile @@ -45,8 +45,30 @@ COPY --from=builder /tmp/build/result /nix/ci-env ENV PATH="/nix/ci-env/bin:$PATH" +# Externally-built dynamically-linked ELF binaries hard-code the loader path +# (e.g. /lib64/ld-linux-x86-64.so.2) in their PT_INTERP header. Copy the +# loader from the Nix store to that path when the base image doesn't already +# provide one (i.e. on nixos/nix). +RUN <&2; exit 1 ;; +esac +if [ ! -e "$target" ]; then + # Use the loader from the same glibc that gcc links libc against, so + # ld-linux and libc/libpthread share GLIBC_PRIVATE symbols at runtime. + src="$(dirname "$(gcc -print-file-name=libc.so.6)")/$(basename "$target")" + [ -e "$src" ] || { echo "ld-linux not found at $src" >&2; exit 1; } + mkdir -p "$(dirname "$target")" + cp "$src" "$target" +fi +EOF + RUN </dev/null && /tmp/check-sanitizers.sh /tmp/cpp_files || true diff --git a/flake.lock b/flake.lock index 5ec053975d..3149f3feed 100644 --- a/flake.lock +++ b/flake.lock @@ -15,7 +15,7 @@ "type": "indirect" } }, - "nixpkgs-glibc231": { + "nixpkgs-custom-glibc": { "flake": false, "locked": { "lastModified": 1593520194, @@ -35,7 +35,7 @@ "root": { "inputs": { "nixpkgs": "nixpkgs", - "nixpkgs-glibc231": "nixpkgs-glibc231" + "nixpkgs-custom-glibc": "nixpkgs-custom-glibc" } } }, diff --git a/flake.nix b/flake.nix index 18671bdf31..3b3ec7ea08 100644 --- a/flake.nix +++ b/flake.nix @@ -6,16 +6,16 @@ # version — matches the system libc on Ubuntu 20.04 LTS. Imported # manually (flake = false) because this revision predates nixpkgs' # own flake.nix. - nixpkgs-glibc231 = { + nixpkgs-custom-glibc = { url = "github:NixOS/nixpkgs/9cd98386a38891d1074fc18036b842dc4416f562"; flake = false; }; }; outputs = - { nixpkgs, nixpkgs-glibc231, ... }: + { nixpkgs, nixpkgs-custom-glibc, ... }: let - forEachSystem = import ./nix/utils.nix { inherit nixpkgs nixpkgs-glibc231; }; + forEachSystem = import ./nix/utils.nix { inherit nixpkgs nixpkgs-custom-glibc; }; in { devShells = forEachSystem (import ./nix/devshell.nix); diff --git a/nix/ci-env.nix b/nix/ci-env.nix index d8021fe0bd..0d617913d9 100644 --- a/nix/ci-env.nix +++ b/nix/ci-env.nix @@ -1,39 +1,102 @@ { pkgs, - glibc231, + customGlibc, ... }: let inherit (import ./packages.nix { inherit pkgs; }) commonPackages; + inherit (pkgs) lib; - # binutils wrapped to emit binaries that reference glibc 2.31 (dynamic - # linker path, library search path, RPATH). - binutils231 = pkgs.wrapBintoolsWith { + # Underlying compiler toolchains to wrap. Bump these in one place to + # roll the whole environment forward. + customGccPackage = pkgs.gcc15; + customLlvmPackages = pkgs.llvmPackages_22; + customClangMajor = lib.versions.major (lib.getVersion customLlvmPackages.clang-unwrapped); + + # binutils wrapped to emit binaries that reference the custom glibc + # (dynamic linker path, library search path, RPATH). + customBinutils = pkgs.wrapBintoolsWith { bintools = pkgs.binutils-unwrapped; - libc = glibc231; + libc = customGlibc; }; - # Rebuild gcc 15 (specifically libstdc++ / libgcc_s) against glibc 2.31. - # The override swaps gcc15.cc's bootstrap stdenv for one that uses the - # existing gcc 15 binary but links against glibc 2.31, so the resulting - # compiler ships runtime libraries that only reference symbols available - # in glibc 2.31. - gcc15CcWithGlibc231 = pkgs.gcc15.cc.override { + # Rebuild gcc (specifically libstdc++ / libgcc_s) against the custom + # glibc. The override swaps gcc.cc's bootstrap stdenv for one that uses + # the existing gcc binary but links against the custom glibc, so the + # resulting compiler ships runtime libraries that only reference symbols + # available in that glibc. + customGccCc = customGccPackage.cc.override { stdenv = pkgs.stdenvAdapters.overrideCC pkgs.stdenv ( pkgs.wrapCCWith { - cc = pkgs.gcc15.cc; - libc = glibc231; - bintools = binutils231; + cc = customGccPackage.cc; + libc = customGlibc; + bintools = customBinutils; } ); }; - # cc-wrapper around the rebuilt compiler, pointing at glibc 2.31 headers - # and libraries. This is what we actually expose to users. - gcc15WithGlibc231 = pkgs.wrapCCWith { - cc = gcc15CcWithGlibc231; - libc = glibc231; - bintools = binutils231; + # cc-wrapper around the rebuilt compiler, pointing at the custom glibc + # headers and libraries. This is what we actually expose to users. + customGcc = pkgs.wrapCCWith { + cc = customGccCc; + libc = customGlibc; + bintools = customBinutils; + }; + + # stdenv built around the rebuilt gcc / custom glibc. Used to rebuild + # compiler-rt below so its sanitizer runtimes see the custom glibc + # headers. + customStdenv = pkgs.stdenvAdapters.overrideCC pkgs.stdenv customGcc; + + # Rebuild compiler-rt against the custom glibc so the sanitizer runtimes + # don't use glibc symbols (or sysconf constants like _SC_SIGSTKSZ) that + # only exist in newer glibc versions. scudo is dropped because its CMake + # includes CheckAtomic with -nostdinc++ in CMAKE_REQUIRED_FLAGS, which + # makes std::atomic unfindable in our stdenv; we don't use scudo (only + # asan/ubsan/tsan etc.). + customCompilerRt = + (customLlvmPackages.compiler-rt.override { + stdenv = customStdenv; + }).overrideAttrs + (old: { + postPatch = (old.postPatch or "") + '' + substituteInPlace lib/CMakeLists.txt \ + --replace-quiet 'add_subdirectory(scudo/standalone)' \ + '# scudo/standalone disabled in xrpld ci-env' + ''; + }); + + # cc-wrapper around clang, pointing at the custom glibc headers and + # libraries. Reuses the rebuilt gcc for libstdc++ / libgcc_s so that + # C++ binaries produced by clang also only reference symbols available + # in the custom glibc. compiler-rt is wired into a resource-root so + # sanitizer runtimes (libclang_rt.*.a) are found at link time; this + # mirrors what nixpkgs does internally when building llvmPackages.clang. + customClang = pkgs.wrapCCWith { + cc = customLlvmPackages.clang-unwrapped; + libc = customGlibc; + bintools = customBinutils; + gccForLibs = customGccCc; + extraPackages = [ customCompilerRt ]; + extraBuildCommands = '' + rsrc="$out/resource-root" + mkdir "$rsrc" + ln -s "${customLlvmPackages.clang-unwrapped.lib}/lib/clang/${customClangMajor}/include" "$rsrc/include" + ln -s "${customCompilerRt.out}/lib" "$rsrc/lib" + ln -s "${customCompilerRt.out}/share" "$rsrc/share" || true + echo "-resource-dir=$rsrc" >> $out/nix-support/cc-cflags + ''; + }; + + # Strip the generic cc/c++/cpp symlinks from the clang wrapper so it can + # coexist with the gcc wrapper in buildEnv. gcc remains the default + # compiler (cc/c++/cpp); clang is invoked explicitly as clang/clang++. + customClangForCiEnv = pkgs.symlinkJoin { + name = "clang-wrapper-custom-for-ci-env"; + paths = [ customClang ]; + postBuild = '' + rm -f $out/bin/cc $out/bin/c++ $out/bin/cpp + ''; }; in @@ -41,8 +104,9 @@ in default = pkgs.buildEnv { name = "xrpld-ci-env"; paths = commonPackages ++ [ - gcc15WithGlibc231 - binutils231 + customGcc + customClangForCiEnv + customBinutils ]; pathsToLink = [ "/bin" diff --git a/nix/packages.nix b/nix/packages.nix index edfe302ec9..d209620a68 100644 --- a/nix/packages.nix +++ b/nix/packages.nix @@ -17,6 +17,7 @@ in llvmPackages_22.clang-tools mold ninja + patchelf perl # needed for openssl pkg-config pre-commit diff --git a/nix/utils.nix b/nix/utils.nix index 07ff169c44..d83e612c16 100644 --- a/nix/utils.nix +++ b/nix/utils.nix @@ -1,4 +1,4 @@ -{ nixpkgs, nixpkgs-glibc231 }: +{ nixpkgs, nixpkgs-custom-glibc }: function: nixpkgs.lib.genAttrs [ @@ -12,10 +12,10 @@ nixpkgs.lib.genAttrs function { pkgs = import nixpkgs { inherit system; }; # glibc 2.31 — matches the system libc on Ubuntu 20.04 LTS. Sourced - # from the nixpkgs snapshot pinned via the `nixpkgs-glibc231` flake - # input, so the build uses the compiler from that snapshot + # from the nixpkgs snapshot pinned via the `nixpkgs-custom-glibc` + # flake input, so the build uses the compiler from that snapshot # (gcc 9.3.0) along with the matching patches, configure flags, and # hardening defaults. - glibc231 = (import nixpkgs-glibc231 { inherit system; }).glibc; + customGlibc = (import nixpkgs-custom-glibc { inherit system; }).glibc; } ) From 633ef4706ffdd0bed445032d2de92e86e59985d6 Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Tue, 26 May 2026 18:32:44 +0200 Subject: [PATCH 04/28] fix: Fix `VaultInvariant` and `VaultDeposit` precision bugs at IOU scale boundaries (#7272) Co-authored-by: Bart --- include/xrpl/protocol/STAmount.h | 1 - include/xrpl/tx/invariants/VaultInvariant.h | 77 ++- src/libxrpl/tx/invariants/VaultInvariant.cpp | 345 ++++++------ .../tx/transactors/vault/VaultDeposit.cpp | 87 +++- src/test/app/Vault_test.cpp | 489 ++++++++++++++++++ src/test/protocol/STAmount_test.cpp | 2 - 6 files changed, 820 insertions(+), 181 deletions(-) diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index bf3e25eedb..a4fffad40c 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -189,7 +189,6 @@ public: /** * Checks if this amount evaluates to zero when constrained to a specific * accounting scale. - * * For XRP and MPT `roundToScale` is a no-op, returns true only when the amount itself is zero. * The `scale` argument is ignored in that case. * For IOU, the amount is rounded to the given scale using Number::RoundingMode::ToNearest mode diff --git a/include/xrpl/tx/invariants/VaultInvariant.h b/include/xrpl/tx/invariants/VaultInvariant.h index ab55cd086a..abc256c880 100644 --- a/include/xrpl/tx/invariants/VaultInvariant.h +++ b/include/xrpl/tx/invariants/VaultInvariant.h @@ -4,9 +4,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -79,16 +81,83 @@ private: std::vector beforeMPTs_; std::unordered_map deltas_; + /** + * @brief Compute the minimum STAmount scale for rounding invariant + * calculations. + * + * Post-amendment (@c fixCleanup3_2_0) this is simply the posterior + * @c assetsTotal scale. Pre-amendment it is the coarsest scale across + * @p vaultDelta and both asset-field deltas. + * + * @param vaultDelta Delta of the vault's asset balance for this transaction. + * @param rules Active ledger rules (used to check the amendment). + * @returns The minimum scale to apply when rounding vault-related amounts. + */ + [[nodiscard]] std::int32_t + computeVaultMinScale(DeltaInfo const& vaultDelta, Rules const& rules) const; + + /** + * @brief Return the vault-asset balance-change delta for an account. + * + * Looks up the ledger-entry delta recorded during @c visitEntry for the + * account entry (XRP), trust line (IOU), or MPToken (MPT) that corresponds + * to the vault asset held by @p id. + * + * @param id Account whose asset delta is requested. + * @returns The delta, or @c std::nullopt if the entry was not touched. + */ + [[nodiscard]] std::optional + deltaAssets(AccountID const& id) const; + + /** + * @brief Return the vault-asset delta for the transaction's sending + * account, adjusted for the fee. + * + * Calls @c deltaAssets for @c tx[sfAccount] and, for non-delegated XRP + * transactions, adds the consumed fee back so the invariant sees the net + * asset movement rather than the fee-reduced balance change. + * + * @param tx The transaction being applied. + * @param fee Fee charged by this transaction. + * @returns The fee-adjusted delta, or @c std::nullopt if the net delta is + * zero or the account entry was not touched. + */ + [[nodiscard]] std::optional + deltaAssetsTxAccount(STTx const& tx, XRPAmount fee) const; + + /** + * @brief Return the vault-share balance-change delta for an account. + * + * For the vault's pseudo-account the @c MPTokenIssuance outstanding-amount + * delta is returned; for all other accounts the @c MPToken delta is + * returned. + * + * @param id Account whose share delta is requested. + * @returns The delta, or @c std::nullopt if the entry was not touched. + */ + [[nodiscard]] std::optional + deltaShares(AccountID const& id) const; + + /** + * @brief Check whether a vault holds no assets. + * + * @param vault Snapshot of the vault to test. + * @returns @c true when both @c assetsAvailable and @c assetsTotal are + * zero. + */ + [[nodiscard]] static bool + isVaultEmpty(Vault const& vault); + public: + // Compute the coarsest scale required to represent all numbers + [[nodiscard]] static std::int32_t + computeCoarsestScale(std::vector const& numbers); + void visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); - - // Compute the coarsest scale required to represent all numbers - [[nodiscard]] static std::int32_t - computeCoarsestScale(std::vector const& numbers); }; } // namespace xrpl diff --git a/src/libxrpl/tx/invariants/VaultInvariant.cpp b/src/libxrpl/tx/invariants/VaultInvariant.cpp index 2947be3bd4..4aa79279a1 100644 --- a/src/libxrpl/tx/invariants/VaultInvariant.cpp +++ b/src/libxrpl/tx/invariants/VaultInvariant.cpp @@ -186,6 +186,101 @@ ValidVault::visitEntry( } } +std::optional +ValidVault::deltaAssets(AccountID const& id) const +{ + auto const& vaultAsset = afterVault_[0].asset; + auto const lookup = [&](uint256 const& key) -> std::optional { + auto const it = deltas_.find(key); + if (it == deltas_.end()) + return std::nullopt; + return it->second; + }; + + return std::visit( + [&](TIss const& issue) -> std::optional { + if constexpr (std::is_same_v) + { + if (isXRP(issue)) + return lookup(keylet::account(id).key); + auto result = lookup(keylet::line(id, issue).key); + // Trust-line balance is stored from the low-account's perspective; + // negate if id is the high account so the delta is in id's terms. + if (result && id > issue.getIssuer()) + result->delta = -result->delta; + return result; + } + else if constexpr (std::is_same_v) + { + return lookup(keylet::mptoken(issue.getMptID(), id).key); + } + }, + vaultAsset.value()); +} + +std::optional +ValidVault::deltaAssetsTxAccount(STTx const& tx, XRPAmount fee) const +{ + auto const& vaultAsset = afterVault_[0].asset; + auto ret = deltaAssets(tx[sfAccount]); + if (!ret.has_value() || !vaultAsset.native()) + return ret; + + if (auto const delegate = tx[~sfDelegate]; delegate.has_value() && *delegate != tx[sfAccount]) + return ret; + + ret->delta += fee.drops(); + if (ret->delta == kZero) + return std::nullopt; + + return ret; +} + +std::optional +ValidVault::deltaShares(AccountID const& id) const +{ + auto const& afterVault = afterVault_[0]; + auto const it = [&]() { + if (id == afterVault.pseudoId) + return deltas_.find(keylet::mptIssuance(afterVault.shareMPTID).key); + return deltas_.find(keylet::mptoken(afterVault.shareMPTID, id).key); + }(); + + return it != deltas_.end() ? std::optional(it->second) : std::nullopt; +} + +bool +ValidVault::isVaultEmpty(Vault const& vault) +{ + return vault.assetsAvailable == 0 && vault.assetsTotal == 0; +} + +std::int32_t +ValidVault::computeVaultMinScale(DeltaInfo const& vaultDelta, Rules const& rules) const +{ + // Returns the posterior `assetsTotal` scale. + // + // 1. Because STAmounts are normalized, `assetsTotal` (being >= `assetsAvailable`) + // safely represents the coarsest exponent needed for both fields. + // + // 2. The scale may decrease (withdraw/clawback) or increase (deposit). In both cases + // we ensure the vault is in a legitimate state in the post-transaction scale. + auto const& afterVault = afterVault_[0]; + auto const& vaultAsset = afterVault.asset; + if (rules.enabled(fixCleanup3_2_0)) + { + NumberRoundModeGuard const roundGuard(Number::RoundingMode::ToNearest); + return scale(afterVault.assetsTotal, vaultAsset); + } + + auto const& beforeVault = beforeVault_[0]; + auto const totalDelta = + DeltaInfo::makeDelta(beforeVault.assetsTotal, afterVault.assetsTotal, vaultAsset); + auto const availableDelta = + DeltaInfo::makeDelta(beforeVault.assetsAvailable, afterVault.assetsAvailable, vaultAsset); + return computeCoarsestScale({vaultDelta, totalDelta, availableDelta}); +} + bool ValidVault::finalize( STTx const& tx, @@ -445,61 +540,6 @@ ValidVault::finalize( } auto const& vaultAsset = afterVault.asset; - auto const deltaAssets = [&](AccountID const& id) -> std::optional { - auto const get = // - [&](auto const& it, std::int8_t sign = 1) -> std::optional { - if (it == deltas_.end()) - return std::nullopt; - - return DeltaInfo{it->second.delta * sign, it->second.scale}; - }; - - return std::visit( - [&](TIss const& issue) { - if constexpr (std::is_same_v) - { - if (isXRP(issue)) - return get(deltas_.find(keylet::account(id).key)); - return get( - deltas_.find(keylet::line(id, issue).key), id > issue.getIssuer() ? -1 : 1); - } - else if constexpr (std::is_same_v) - { - return get(deltas_.find(keylet::mptoken(issue.getMptID(), id).key)); - } - }, - vaultAsset.value()); - }; - auto const deltaAssetsTxAccount = [&]() -> std::optional { - auto ret = deltaAssets(tx[sfAccount]); - // Nothing returned or not XRP transaction - if (!ret.has_value() || !vaultAsset.native()) - return ret; - - // Delegated transaction; no need to compensate for fees - if (auto const delegate = tx[~sfDelegate]; - delegate.has_value() && *delegate != tx[sfAccount]) - return ret; - - ret->delta += fee.drops(); - if (ret->delta == kZero) - return std::nullopt; - - return ret; - }; - auto const deltaShares = [&](AccountID const& id) -> std::optional { - auto const it = [&]() { - if (id == afterVault.pseudoId) - return deltas_.find(keylet::mptIssuance(afterVault.shareMPTID).key); - return deltas_.find(keylet::mptoken(afterVault.shareMPTID, id).key); - }(); - - return it != deltas_.end() ? std::optional(it->second) : std::nullopt; - }; - - auto const vaultHoldsNoAssets = [&](Vault const& vault) { - return vault.assetsAvailable == 0 && vault.assetsTotal == 0; - }; // Technically this does not need to be a lambda, but it's more // convenient thanks to early "return false"; the not-so-nice @@ -629,16 +669,8 @@ ValidVault::finalize( return false; // That's all we can do } - // Get the coarsest scale to round calculations to - auto const totalDelta = DeltaInfo::makeDelta( - beforeVault.assetsTotal, afterVault.assetsTotal, vaultAsset); - auto const availableDelta = DeltaInfo::makeDelta( - beforeVault.assetsAvailable, afterVault.assetsAvailable, vaultAsset); - auto const minScale = computeCoarsestScale({ - *maybeVaultDeltaAssets, - totalDelta, - availableDelta, - }); + // Get the posterior scale to round calculations to + auto const minScale = computeVaultMinScale(*maybeVaultDeltaAssets, view.rules()); auto const vaultDeltaAssets = roundToAsset(vaultAsset, maybeVaultDeltaAssets->delta, minScale); @@ -669,12 +701,11 @@ ValidVault::finalize( if (!issuerDeposit) { - auto const maybeAccDeltaAssets = deltaAssetsTxAccount(); + auto const maybeAccDeltaAssets = deltaAssetsTxAccount(tx, fee); if (!maybeAccDeltaAssets) { - JLOG(j.fatal()) << // - "Invariant failed: deposit must change depositor " - "balance"; + JLOG(j.fatal()) + << "Invariant failed: deposit must change depositor balance"; return false; } auto const localMinScale = @@ -685,19 +716,20 @@ ValidVault::finalize( auto const localVaultDeltaAssets = roundToAsset(vaultAsset, vaultDeltaAssets, localMinScale); + // For IOUs, if the deposit amount is not-representable at depositor trustline + // scale deposit amount could round to zero, giving depositor shares for no + // assets. Unlike withdrawal, we do not allow that. if (accountDeltaAssets >= kZero) { - JLOG(j.fatal()) << // - "Invariant failed: deposit must decrease depositor " - "balance"; + JLOG(j.fatal()) + << "Invariant failed: deposit must decrease depositor balance"; result = false; } if (localVaultDeltaAssets * -1 != accountDeltaAssets) { - JLOG(j.fatal()) << // - "Invariant failed: deposit must change vault and " - "depositor balance by equal amount"; + JLOG(j.fatal()) << "Invariant failed: " << // + "deposit must change vault and depositor balance by equal amount"; result = false; } } @@ -705,45 +737,38 @@ ValidVault::finalize( if (afterVault.assetsMaximum > kZero && afterVault.assetsTotal > afterVault.assetsMaximum) { - JLOG(j.fatal()) << // - "Invariant failed: deposit assets outstanding must not " - "exceed assets maximum"; + JLOG(j.fatal()) << "Invariant failed: " << // + "deposit assets outstanding must not exceed assets maximum"; result = false; } auto const maybeAccDeltaShares = deltaShares(tx[sfAccount]); if (!maybeAccDeltaShares) { - JLOG(j.fatal()) << // - "Invariant failed: deposit must change depositor " - "shares"; + JLOG(j.fatal()) << "Invariant failed: deposit must change depositor shares"; return false; // That's all we can do } - // We don't need to round shares, they are integral MPT + // We don't round shares, they are integral MPT auto const& accountDeltaShares = *maybeAccDeltaShares; if (accountDeltaShares.delta <= kZero) { - JLOG(j.fatal()) << // - "Invariant failed: deposit must increase depositor " - "shares"; + JLOG(j.fatal()) << "Invariant failed: deposit must increase depositor shares"; result = false; } auto const maybeVaultDeltaShares = deltaShares(afterVault.pseudoId); if (!maybeVaultDeltaShares || maybeVaultDeltaShares->delta == kZero) { - JLOG(j.fatal()) << // - "Invariant failed: deposit must change vault shares"; + JLOG(j.fatal()) << "Invariant failed: deposit must change vault shares"; return false; // That's all we can do } - // We don't need to round shares, they are integral MPT + // We don't round shares, they are integral MPT auto const& vaultDeltaShares = *maybeVaultDeltaShares; if (vaultDeltaShares.delta * -1 != accountDeltaShares.delta) { - JLOG(j.fatal()) << // - "Invariant failed: deposit must change depositor and " - "vault shares by equal amount"; + JLOG(j.fatal()) << "Invariant failed: " << // + "deposit must change depositor and vault shares by equal amount"; result = false; } @@ -751,8 +776,8 @@ ValidVault::finalize( vaultAsset, afterVault.assetsTotal - beforeVault.assetsTotal, minScale); if (assetTotalDelta != vaultDeltaAssets) { - JLOG(j.fatal()) << "Invariant failed: deposit and assets " - "outstanding must add up"; + JLOG(j.fatal()) + << "Invariant failed: deposit and assets outstanding must add up"; result = false; } @@ -760,8 +785,7 @@ ValidVault::finalize( vaultAsset, afterVault.assetsAvailable - beforeVault.assetsAvailable, minScale); if (assetAvailableDelta != vaultDeltaAssets) { - JLOG(j.fatal()) << "Invariant failed: deposit and assets " - "available must add up"; + JLOG(j.fatal()) << "Invariant failed: deposit and assets available must add up"; result = false; } @@ -772,34 +796,25 @@ ValidVault::finalize( XRPL_ASSERT( !beforeVault_.empty(), - "xrpl::ValidVault::finalize : withdrawal updated a " - "vault"); + "xrpl::ValidVault::finalize : withdrawal updated a vault"); auto const& beforeVault = beforeVault_[0]; auto const maybeVaultDeltaAssets = deltaAssets(afterVault.pseudoId); - if (!maybeVaultDeltaAssets) { - JLOG(j.fatal()) << "Invariant failed: withdrawal must " - "change vault balance"; + JLOG(j.fatal()) << "Invariant failed: withdrawal must change vault balance"; return false; // That's all we can do } - // Get the most coarse scale to round calculations to - auto const totalDelta = DeltaInfo::makeDelta( - beforeVault.assetsTotal, afterVault.assetsTotal, vaultAsset); - auto const availableDelta = DeltaInfo::makeDelta( - beforeVault.assetsAvailable, afterVault.assetsAvailable, vaultAsset); - auto const minScale = - computeCoarsestScale({*maybeVaultDeltaAssets, totalDelta, availableDelta}); + // Get the posterior scale to round calculations to + auto const minScale = computeVaultMinScale(*maybeVaultDeltaAssets, view.rules()); auto const vaultPseudoDeltaAssets = roundToAsset(vaultAsset, maybeVaultDeltaAssets->delta, minScale); if (vaultPseudoDeltaAssets >= kZero) { - JLOG(j.fatal()) << "Invariant failed: withdrawal must " - "decrease vault balance"; + JLOG(j.fatal()) << "Invariant failed: withdrawal must decrease vault balance"; result = false; } @@ -814,7 +829,7 @@ ValidVault::finalize( if (!issuerWithdrawal) { - auto const maybeAccDelta = deltaAssetsTxAccount(); + auto const maybeAccDelta = deltaAssetsTxAccount(tx, fee); auto const maybeOtherAccDelta = [&]() -> std::optional { if (auto const destination = tx[~sfDestination]; destination && *destination != tx[sfAccount]) @@ -825,8 +840,7 @@ ValidVault::finalize( if (maybeAccDelta.has_value() == maybeOtherAccDelta.has_value()) { JLOG(j.fatal()) << // - "Invariant failed: withdrawal must change one " - "destination balance"; + "Invariant failed: withdrawal must change one destination balance"; return false; } @@ -835,63 +849,83 @@ ValidVault::finalize( // the scale of destinationDelta can be coarser than // minScale, so we take that into account when rounding - auto const localMinScale = - std::max(minScale, computeCoarsestScale({destinationDelta})); + auto const destinationScale = computeCoarsestScale({destinationDelta}); + auto const localMinScale = std::max(minScale, destinationScale); auto const roundedDestinationDelta = roundToAsset(vaultAsset, destinationDelta.delta, localMinScale); - if (roundedDestinationDelta <= kZero) + // Post-fixCleanup3_2_0: Tolerate zero-rounded destination deltas for IOUs only. + // If the receiver's trust line sits at a coarser scale, the inflow may + // safely round down to zero. + // + // XRP and MPT remain strict. Because they are integer-exact, a zero + // destination delta indicates a true accounting bug, not a rounding artifact. + bool const tolerateZeroDelta = + view.rules().enabled(fixCleanup3_2_0) && !vaultAsset.integral(); + auto const invalidBalanceChange = tolerateZeroDelta + ? roundedDestinationDelta < kZero + : roundedDestinationDelta <= kZero; + if (invalidBalanceChange) { JLOG(j.fatal()) << // - "Invariant failed: withdrawal must increase " - "destination balance"; + "Invariant failed: withdrawal must increase destination balance"; result = false; } auto const localPseudoDeltaAssets = roundToAsset(vaultAsset, vaultPseudoDeltaAssets, localMinScale); - if (localPseudoDeltaAssets * -1 != roundedDestinationDelta) + // For IOU assets near a precision boundary the destination's STAmount + // exponent can shift, making part of the sent value unrepresentable at the + // receiver's new scale — that portion is irreversibly absorbed by the IOU + // rail. Tolerate the mismatch only when the destroyed amount (vault outflow + // minus destination inflow, in Number space) is itself sub-ULP at the + // destination's scale. Floor rounding is used so that values exactly at the + // step boundary are not mistakenly dismissed. Any representable discrepancy + // indicates a real accounting bug and must be caught. + auto const destroyedIsSubUlp = tolerateZeroDelta && + roundToAsset( + vaultAsset, + maybeVaultDeltaAssets->delta * -1 - destinationDelta.delta, + destinationScale, + Number::RoundingMode::Downward) == kZero; + if (!destroyedIsSubUlp && + localPseudoDeltaAssets * -1 != roundedDestinationDelta) { - JLOG(j.fatal()) << // - "Invariant failed: withdrawal must change vault " - "and destination balance by equal amount"; + JLOG(j.fatal()) << "Invariant failed: " << // + "withdrawal must change vault and destination balance by equal " + "amount"; result = false; } } - // We don't need to round shares, they are integral MPT + // We don't round shares, they are integral MPT auto const accountDeltaShares = deltaShares(tx[sfAccount]); if (!accountDeltaShares) { - JLOG(j.fatal()) << // - "Invariant failed: withdrawal must change depositor " - "shares"; + JLOG(j.fatal()) << "Invariant failed: withdrawal must change depositor shares"; return false; } if (accountDeltaShares->delta >= kZero) { - JLOG(j.fatal()) << // - "Invariant failed: withdrawal must decrease depositor " - "shares"; + JLOG(j.fatal()) + << "Invariant failed: withdrawal must decrease depositor shares"; result = false; } - // We don't need to round shares, they are integral MPT + // We don't round shares, they are integral MPT auto const vaultDeltaShares = deltaShares(afterVault.pseudoId); if (!vaultDeltaShares || vaultDeltaShares->delta == kZero) { - JLOG(j.fatal()) << // - "Invariant failed: withdrawal must change vault shares"; + JLOG(j.fatal()) << "Invariant failed: withdrawal must change vault shares"; return false; // That's all we can do } if (vaultDeltaShares->delta * -1 != accountDeltaShares->delta) { - JLOG(j.fatal()) << // - "Invariant failed: withdrawal must change depositor " - "and vault shares by equal amount"; + JLOG(j.fatal()) << "Invariant failed: " << // + "withdrawal must change depositor and vault shares by equal amount"; result = false; } @@ -900,8 +934,8 @@ ValidVault::finalize( // Note, vaultBalance is negative (see check above) if (assetTotalDelta != vaultPseudoDeltaAssets) { - JLOG(j.fatal()) << "Invariant failed: withdrawal and " - "assets outstanding must add up"; + JLOG(j.fatal()) + << "Invariant failed: withdrawal and assets outstanding must add up"; result = false; } @@ -910,8 +944,8 @@ ValidVault::finalize( if (assetAvailableDelta != vaultPseudoDeltaAssets) { - JLOG(j.fatal()) << "Invariant failed: withdrawal and " - "assets available must add up"; + JLOG(j.fatal()) + << "Invariant failed: withdrawal and assets available must add up"; result = false; } @@ -929,12 +963,11 @@ ValidVault::finalize( // The owner can use clawback to force-burn shares when the // vault is empty but there are outstanding shares if (!(beforeShares && beforeShares->sharesTotal > 0 && - vaultHoldsNoAssets(beforeVault) && beforeVault.owner == tx[sfAccount])) + isVaultEmpty(beforeVault) && beforeVault.owner == tx[sfAccount])) { - JLOG(j.fatal()) << // - "Invariant failed: clawback may only be performed " - "by the asset issuer, or by the vault owner of an " - "empty vault"; + JLOG(j.fatal()) << "Invariant failed: " << // + "clawback may only be performed by the asset issuer, or by the vault " + "owner of an empty vault"; return false; // That's all we can do } } @@ -942,19 +975,13 @@ ValidVault::finalize( auto const maybeVaultDeltaAssets = deltaAssets(afterVault.pseudoId); if (maybeVaultDeltaAssets) { - auto const totalDelta = DeltaInfo::makeDelta( - beforeVault.assetsTotal, afterVault.assetsTotal, vaultAsset); - auto const availableDelta = DeltaInfo::makeDelta( - beforeVault.assetsAvailable, afterVault.assetsAvailable, vaultAsset); auto const minScale = - computeCoarsestScale({*maybeVaultDeltaAssets, totalDelta, availableDelta}); + computeVaultMinScale(*maybeVaultDeltaAssets, view.rules()); auto const vaultDeltaAssets = roundToAsset(vaultAsset, maybeVaultDeltaAssets->delta, minScale); if (vaultDeltaAssets >= kZero) { - JLOG(j.fatal()) << // - "Invariant failed: clawback must decrease vault " - "balance"; + JLOG(j.fatal()) << "Invariant failed: clawback must decrease vault balance"; result = false; } @@ -963,8 +990,7 @@ ValidVault::finalize( if (assetsTotalDelta != vaultDeltaAssets) { JLOG(j.fatal()) << // - "Invariant failed: clawback and assets outstanding " - "must add up"; + "Invariant failed: clawback and assets outstanding must add up"; result = false; } @@ -975,12 +1001,11 @@ ValidVault::finalize( if (assetAvailableDelta != vaultDeltaAssets) { JLOG(j.fatal()) << // - "Invariant failed: clawback and assets available " - "must add up"; + "Invariant failed: clawback and assets available must add up"; result = false; } } - else if (!vaultHoldsNoAssets(beforeVault)) + else if (!isVaultEmpty(beforeVault)) { JLOG(j.fatal()) << // "Invariant failed: clawback must change vault balance"; @@ -998,8 +1023,7 @@ ValidVault::finalize( if (maybeAccountDeltaShares->delta >= kZero) { JLOG(j.fatal()) << // - "Invariant failed: clawback must decrease holder " - "shares"; + "Invariant failed: clawback must decrease holder shares"; result = false; } @@ -1014,9 +1038,8 @@ ValidVault::finalize( if (vaultDeltaShares->delta * -1 != maybeAccountDeltaShares->delta) { - JLOG(j.fatal()) << // - "Invariant failed: clawback must change holder and " - "vault shares by equal amount"; + JLOG(j.fatal()) << "Invariant failed: " << // + "clawback must change holder and vault shares by equal amount"; result = false; } diff --git a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp index aaaf3cced8..50d165a2ba 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include #include // IWYU pragma: keep #include @@ -26,6 +28,24 @@ namespace xrpl { +[[nodiscard]] +static STAmount +roundToVaultScale(STAmount const& amount, SLE::const_ref vault) +{ + XRPL_ASSERT(vault && vault->getType() == ltVAULT, "xrpl::roundToVaultScale : valid vault sle"); + XRPL_ASSERT( + amount.asset() == vault->at(sfAsset), "xrpl::roundToVaultScale : valid vault asset"); + + if (amount.integral()) + return amount; + + int const postScale = [&]() { + NumberRoundModeGuard const rg(Number::RoundingMode::ToNearest); + return scale(vault->at(sfAssetsTotal) + amount, vault->at(sfAsset)); + }(); + return roundToScale(amount, postScale, Number::RoundingMode::Downward); +} + NotTEC VaultDeposit::preflight(PreflightContext const& ctx) { @@ -49,9 +69,9 @@ VaultDeposit::preclaim(PreclaimContext const& ctx) return tecNO_ENTRY; auto const& account = ctx.tx[sfAccount]; - auto const assets = ctx.tx[sfAmount]; + auto const amount = ctx.tx[sfAmount]; auto const vaultAsset = vault->at(sfAsset); - if (assets.asset() != vaultAsset) + if (amount.asset() != vaultAsset) return tecWRONG_ASSET; auto const& vaultAccount = vault->at(sfAccount); @@ -63,7 +83,7 @@ VaultDeposit::preclaim(PreclaimContext const& ctx) auto const mptIssuanceID = vault->at(sfShareMPTID); auto const vaultShare = MPTIssue(mptIssuanceID); - if (vaultShare == assets.asset()) + if (vaultShare == amount.asset()) { // LCOV_EXCL_START JLOG(ctx.j.error()) << "VaultDeposit: vault shares and assets cannot be same."; @@ -122,28 +142,69 @@ VaultDeposit::preclaim(PreclaimContext const& ctx) if (auto const ter = requireAuth(ctx.view, vaultAsset, account); !isTesSuccess(ter)) return ter; - if (accountHolds( - ctx.view, - account, - vaultAsset, - FreezeHandling::ZeroIfFrozen, - AuthHandling::ZeroIfUnauthorized, - ctx.j, - SpendableHandling::FullBalance) < assets) + bool const fix320Enabled = ctx.view.rules().enabled(fixCleanup3_2_0); + auto const roundedAmount = fix320Enabled ? roundToVaultScale(amount, vault) : amount; + + if (fix320Enabled && roundedAmount == beast::kZero) + { + JLOG(ctx.j.warn()) << "VaultDeposit: deposit amount: " << ctx.tx[sfAmount] + << " is zero at vault scale"; + return tecPRECISION_LOSS; + } + + auto const accountBalance = accountHolds( + ctx.view, + account, + vaultAsset, + FreezeHandling::ZeroIfFrozen, + AuthHandling::ZeroIfUnauthorized, + ctx.j, + SpendableHandling::FullBalance); + + if (accountBalance < roundedAmount) return tecINSUFFICIENT_FUNDS; + // IOU precision checks + if (fix320Enabled && !roundedAmount.integral()) + { + // reject deposits that would canonicalize to a no-op at the depositor's trustline scale. + // Skipped for issuer-as-depositor: accountHolds returns (kMaxValue @ kMaxOffset) which + // would always trip the predicate. + if (account != amount.getIssuer() && + amount.isZeroAtScale(scale(accountBalance, vaultAsset))) + { + JLOG(ctx.j.warn()) << "VaultDeposit: amount " << amount.getFullText() + << " rounds to zero at counterparty trust-line scale"; + return tecPRECISION_LOSS; + } + } + return tesSUCCESS; } TER VaultDeposit::doApply() { + bool const fix320Enabled = view().rules().enabled(fixCleanup3_2_0); auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID])); if (!vault) return tefINTERNAL; // LCOV_EXCL_LINE auto const vaultAsset = vault->at(sfAsset); - auto const amount = ctx_.tx[sfAmount]; + // Post-amendment IOU only: round Downward to the AssetsTotal precision so + // a sub-ULP tail can't be silently absorbed by one rail and not the other. + auto const amount = + fix320Enabled ? roundToVaultScale(ctx_.tx[sfAmount], vault) : ctx_.tx[sfAmount]; + + // We validated zero-amount in preclaim, if we ended up with zero now, fail hard. + if (amount == beast::kZero) + { + // LCOV_EXCL_START + JLOG(j_.error()) << "VaultDeposit: deposit amount: " << ctx_.tx[sfAmount] << " is zero"; + return tecINTERNAL; + // LCOV_EXCL_STOP + } + // Make sure the depositor can hold shares. auto const mptIssuanceID = (*vault)[sfShareMPTID]; auto const sleIssuance = view().read(keylet::mptIssuance(mptIssuanceID)); @@ -259,7 +320,7 @@ VaultDeposit::doApply() // trust line into debt the exact case preclaim authorizes via SpendableHandling::FullBalance. // The check thus converts a preclaim- authorized deposit into tefINTERNAL after the asset // transfer. - if (!view().rules().enabled(fixCleanup3_2_0)) + if (!fix320Enabled) { // Sanity check if (accountHolds( diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index c6a9a54a53..d8527876f1 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -6457,6 +6457,489 @@ class Vault_test : public beast::unit_test::Suite runTest(amendments); } + // Bug: DeltaInfo::makeDelta uses max(scale(after), scale(before)) for the + // sfAssetsTotal and sfAssetsAvailable deltas, and visitEntry applies the + // same max() for the vault pseudo-account RippleState. When + // sfAssetsTotal sits exactly at 1e16 (IOU exponent 1, ULP = 10) and a + // withdrawal of 5 USD brings it to 9.999...995e15 (IOU exponent 0, + // ULP = 1), all three computations pick the anterior coarser scale 1. + // roundToAsset(-5, scale=1) collapses to 0, so the invariant check + // vaultPseudoDeltaAssets >= kZero fires even though the state change is + // valid and fully consistent at IOU precision. + // + // Fix (fixCleanup3_2_0): finalize compares the vault pseudo-account and + // sfAssetsTotal/Available deltas directly in Number space, bypassing + // scale-coarsened rounding. + void + testBugMakeDeltaAnteriorScale() + { + using namespace test::jtx; + + auto runScenario = [this](FeatureBitset features, TER expected) { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const alice{"alice"}; + + env.fund(XRP(100'000), issuer, alice); + env.close(); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const usd{issuer["USD"]}; + // Trust limit of 2e16, fund exactly 1e16 so deposit lands at the + // IOU scale-1 boundary (exponent 1, ULP = 10). + STAmount const fundAndDeposit{usd.raw(), Number{1, 16}}; + + env(trust(alice, STAmount{usd.raw(), 2, 16})); + env.close(); + env(pay(issuer, alice, fundAndDeposit)); + env.close(); + + Vault const vault{env}; + auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = usd}); + vaultTx[sfScale] = 0; + env(vaultTx); + env.close(); + + // sfAssetsTotal = sfAssetsAvailable = 1e16 (exponent 1, ULP = 10). + env(vault.deposit( + {.depositor = alice, .id = vaultKeylet.key, .amount = fundAndDeposit})); + env.close(); + + // Withdraw 5 USD: -5 is sub-ULP at the anterior scale (ULP = 10) + // but exact at the posterior scale (ULP = 1). The state change is + // consistent; only the invariant's scale selection is wrong. + env(vault.withdraw({.depositor = alice, .id = vaultKeylet.key, .amount = usd(5)}), + Ter(expected)); + env.close(); + }; + + { + testcase( + "bug: VaultWithdraw across IOU scale boundary fires invariant " + "(pre-fixCleanup3_2_0)"); + runScenario(testableAmendments() - fixCleanup3_2_0, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultWithdraw across IOU scale boundary succeeds " + "(post-fixCleanup3_2_0)"); + runScenario(testableAmendments(), tesSUCCESS); + } + } + + // Bug: DeltaInfo::makeDelta uses max(scale(after), scale(before)) for + // sfAssetsTotal/Available deltas. This is symmetric to + // testBugMakeDeltaAnteriorScale but in the opposite direction: a deposit + // pushes assetsTotal from just below 1e16 (IOU exponent 0, ULP = 1) to just + // above it (exponent 1, ULP = 10). makeDelta picks the coarser *posterior* + // scale 1. The trust line balance rounds from atEdge + 2 = 10,000,000,000,000,001 + // → 1e16, so the pseudo-account delta is only +1 in IOU space. + // roundToAsset(+1, scale=1) = 0 fires "deposit must increase vault balance" + // even though the state change is consistent at every precision boundary. + // + // Fix (fixCleanup3_2_0): computeVaultMinScale uses the posterior Number-space + // scale of sfAssetsTotal (which retains the full value 10,000,000,000,000,001, + // exponent 0), giving minScale = 0. roundToAsset(+1, scale=0) = 1 > 0 and + // the invariant passes. However the transactor's own precision guard fires + // first (bob pays 2 USD, vault receives only 1 due to IOU rounding), so the + // post-amendment result is tecPRECISION_LOSS rather than tesSUCCESS — + // the depositor is protected from silently losing 1 USD to rounding. + void + testBugMakeDeltaPosteriorScale() + { + using namespace test::jtx; + + auto runScenario = [this](FeatureBitset features, TER expected) { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + + env.fund(XRP(100'000), issuer, alice, bob); + env.close(); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const usd{issuer["USD"]}; + // atEdge is the largest IOU value with exponent 0 (ULP = 1). + // A deposit of 2 USD brings assetsTotal to 10,000,000,000,000,001 + // in Number space, crossing the 1e16 boundary in IOU space. + STAmount const atEdge{usd.raw(), Number{9'999'999'999'999'999LL}}; + + env(trust(alice, STAmount{usd.raw(), 2, 16})); + env(trust(bob, usd(100))); + env.close(); + env(pay(issuer, alice, atEdge)); + env(pay(issuer, bob, usd(2))); + env.close(); + + Vault const vault{env}; + auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = usd}); + vaultTx[sfScale] = 0; + env(vaultTx); + env.close(); + + // sfAssetsTotal = sfAssetsAvailable = atEdge (exponent 0, ULP = 1) + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = atEdge})); + env.close(); + + // Deposit 2 USD: +2 is sub-ULP at the posterior IOU scale (ULP = 10) + // but exact at the Number scale retained by sfAssetsTotal. + env(vault.deposit({.depositor = bob, .id = vaultKeylet.key, .amount = usd(2)}), + Ter(expected)); + env.close(); + }; + + { + testcase( + "bug: VaultDeposit across IOU scale boundary fires invariant " + "(pre-fixCleanup3_2_0)"); + runScenario(testableAmendments() - fixCleanup3_2_0, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultDeposit across IOU scale boundary succeeds " + "(post-fixCleanup3_2_0)"); + runScenario(testableAmendments(), tecPRECISION_LOSS); + } + } + + // Bug: ValidVault::visitEntry computes destinationDelta.scale as + // max(before_exponent, after_exponent) for RippleState entries. When a + // withdrawal credits a destination whose IOU balance sits just below a + // power-of-10 boundary (atEdge = 9'999'999'999'999'999), the post-credit + // STAmount rounds up one exponent (exponent 0 → 1), making + // destinationDelta.scale = 1. The invariant then calls + // roundToAsset(+2 USD, scale=1) = 0 and incorrectly fires + // "withdrawal must increase destination balance". + // + // Fix (fixCleanup3_2_0): finalize compares destination delta directly in + // Number space, bypassing scale-coarsened rounding. The transaction + // itself succeeds because the effective IOU credit is non-trivial at + // Number precision even though the STAmount exponent shifted. + void + testVaultWithdrawCanonicalizeToZero() + { + using namespace test::jtx; + + enum class DestKind : bool { ThirdParty = false, Self = true }; + + auto runScenario = [this](FeatureBitset features, DestKind destKind, TER expected) { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + + env.fund(XRP(100'000), issuer, alice, bob); + env.close(); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const usd{issuer["USD"]}; + STAmount const aliceLimit{usd.raw(), 2, 16}; + STAmount const bobLimit{usd.raw(), 2, 16}; + STAmount const atEdge{usd.raw(), Number{9'999'999'999'999'999LL}}; + + env(trust(alice, aliceLimit)); + if (destKind == DestKind::ThirdParty) + env(trust(bob, bobLimit)); + env.close(); + + env(pay(issuer, alice, usd(1'000))); + if (destKind == DestKind::ThirdParty) + env(pay(issuer, bob, atEdge)); + env.close(); + + Vault const vault{env}; + auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = usd}); + vaultTx[sfScale] = 0; + env(vaultTx); + env.close(); + + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = usd(1'000)})); + env.close(); + + // For the self-destination case, push alice's own trust line to + // the IOU edge so the next withdraw inflow crosses the boundary. + if (destKind == DestKind::Self) + { + env(pay(issuer, alice, atEdge)); + env.close(); + } + + auto tx = vault.withdraw({.depositor = alice, .id = vaultKeylet.key, .amount = usd(2)}); + if (destKind == DestKind::ThirdParty) + tx[sfDestination] = bob.human(); + env(tx, Ter(expected)); + env.close(); + }; + + { + testcase( + "bug: VaultWithdraw to third-party at IOU edge fires invariant " + "(pre-fixCleanup3_2_0)"); + runScenario( + testableAmendments() - fixCleanup3_2_0, DestKind::ThirdParty, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultWithdraw to third-party at IOU edge succeeds " + "(post-fixCleanup3_2_0)"); + runScenario(testableAmendments(), DestKind::ThirdParty, tesSUCCESS); + } + { + testcase( + "bug: VaultWithdraw to self at IOU edge fires invariant " + "(pre-fixCleanup3_2_0)"); + runScenario( + testableAmendments() - fixCleanup3_2_0, DestKind::Self, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultWithdraw to self at IOU edge succeeds " + "(post-fixCleanup3_2_0)"); + runScenario(testableAmendments(), DestKind::Self, tesSUCCESS); + } + } + + // Bug: the equality check (vault outflow == destination inflow) was + // skipped whenever the destination delta rounded to zero at localMinScale, + // including cases where the vault outflow rounded to a non-zero value and + // a representable amount of value was genuinely destroyed. + // + // Scenario: Bob's IOU balance sits 5 units below the 10^16 STAmount + // precision boundary (atEdge2 = 9,999,999,999,999,995). A withdrawal of + // 6 USD shifts his balance across that boundary: the exponent increments + // (0 → 1), so his effective inflow in Number space is only +5 — 1 USD is + // consumed by the precision-boundary rounding and cannot be credited. + // + // The destroyed amount (1 USD) is sub-ULP at destinationScale=1 (step=10), + // so the check treats it as an unavoidable IOU-precision artefact and + // lets the transaction succeed. + // + // Contrast: if 15 USD were destroyed at the same scale (destroyed ≥ step), + // floor(15/10)=1 ≠ 0 and the invariant would fire — that discrepancy IS + // representable and indicates a real accounting bug. + // + // Pre-fixCleanup3_2_0: the "must increase destination balance" check fires + // because roundedDestinationDelta = 0 ≤ 0. + void + testVaultWithdrawEqualityEnforced() + { + using namespace test::jtx; + + auto runScenario = [this](FeatureBitset features, TER expected) { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + + env.fund(XRP(100'000), issuer, alice, bob); + env.close(); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const usd{issuer["USD"]}; + STAmount const aliceLimit{usd.raw(), 2, 16}; + STAmount const bobLimit{usd.raw(), 2, 16}; + // Bob's balance sits 5 units below the 10^16 STAmount precision + // boundary. Receiving 6 USD shifts his exponent 0 → 1; the + // STAmount records +5, not +6 (1 USD is lost to rounding). + STAmount const atEdge2{usd.raw(), Number{9'999'999'999'999'995LL}}; + + env(trust(alice, aliceLimit)); + env(trust(bob, bobLimit)); + env.close(); + + env(pay(issuer, alice, usd(1'000))); + env(pay(issuer, bob, atEdge2)); + env.close(); + + Vault const vault{env}; + auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = usd}); + vaultTx[sfScale] = 0; + env(vaultTx); + env.close(); + + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = usd(1'000)})); + env.close(); + + // Withdraw 6 USD to Bob: vault loses 6, Bob gains only 5. + // Destroyed amount = 1 USD, which is sub-ULP at destinationScale=1. + auto tx = vault.withdraw({.depositor = alice, .id = vaultKeylet.key, .amount = usd(6)}); + tx[sfDestination] = bob.human(); + env(tx, Ter(expected)); + env.close(); + }; + + { + testcase( + "bug: VaultWithdraw to destination at IOU precision boundary fires " + "invariant (pre-fixCleanup3_2_0)"); + runScenario(testableAmendments() - fixCleanup3_2_0, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultWithdraw to destination at IOU precision boundary succeeds " + "when destroyed amount is sub-ULP (post-fixCleanup3_2_0)"); + runScenario(testableAmendments(), tesSUCCESS); + } + } + + // Bug: when a depositor's IOU trustline balance is very large (e.g. + // ~1e17), adding a small deposit (e.g. 1 USD) leaves sfAssetsTotal + // unchanged at IOU precision because the increment is sub-ULP at the + // vault's current asset scale. The vault records the deposit, mints + // shares, and decrements the depositor's trustline, but sfAssetsTotal + // does not change — the conservation invariant fires because the rail + // delta is zero. + // + // Two sub-cases are exercised: + // 1. First-ever deposit into an empty vault: the depositor's own + // trustline has a large balance so 1 USD canonicalizes to zero + // when written back through the IOU rail. + // 2. Subsequent deposit after the vault already holds a large + // sfAssetsTotal: a different depositor (bob, with a small balance) + // sends 1 USD, which again rounds to zero at the vault's coarse + // asset scale. + // + // Fix (fixCleanup3_2_0): the deposit transactor checks whether + // roundToAsset(amount, vault_scale) == 0 and rejects early with + // tecPRECISION_LOSS before any state is modified. + void + testVaultDepositCanonicalizeToZero() + { + using namespace test::jtx; + auto runScenario = [this](FeatureBitset features, TER expected) { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + + env.fund(XRP(100'000), issuer, alice, bob); + env.close(); + + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const usd{issuer["USD"]}; + + STAmount const trustLimit{usd.raw(), Number{99'999'999'999'999'999LL}}; + STAmount const aliceFund{usd.raw(), Number{99'999'999'999'999'999LL}}; + + env(trust(alice, trustLimit)); + env(trust(bob, trustLimit)); + env.close(); + + env(pay(issuer, alice, aliceFund)); + env(pay(issuer, bob, usd(1000))); + env.close(); + + Vault const vault{env}; + + // Scale=0 so sfAssetsTotal stores whole USD + auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = usd}); + vaultTx[sfScale] = 0; + env(vaultTx); + env.close(); + + // Alice's deposit canonicalizes to zero at her own trustline scale + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = usd(1)}), + Ter(expected)); + + // Increase vault-scale + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = aliceFund})); + env.close(); + + env(vault.deposit({.depositor = bob, .id = vaultKeylet.key, .amount = usd(1)}), + Ter(expected)); + env.close(); + }; + + { + testcase( + "bug: VaultDeposit below Vault precision canonicalized to zero " + "(pre-fixCleanup3_2_0)"); + runScenario(testableAmendments() - fixCleanup3_2_0, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultDeposit below Vault precision canonicalized to zero " + "(post-fixCleanup3_2_0)"); + runScenario(testableAmendments(), tecPRECISION_LOSS); + } + } + + // VaultDeposit by issuer with the vault parked at the IOU 16-digit + // edge (9.999e15). Issuer mints 2 more USD; the vault trust line + // goes 9.999e15 → 10^16, gaining 1 unit instead of 2 (canonicalization). + // + // Pre-fixCleanup3_2_0: the proactive check is absent; the deposit + // applies, then VaultInvariant's "deposit must increase vault + // balance" assertion fires at finalize time on the rounded vault + // delta of zero, returning tecINVARIANT_FAILED. + // Post-amendment: reject deposit that is not representable at Vault scale. + void + testBugIssuerVaultDepositAtEdge() + { + using namespace test::jtx; + + auto runScenario = [this](FeatureBitset features, TER expected) { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const owner{"owner"}; + + env.fund(XRP(100'000), issuer, owner); + env.close(); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const usd{issuer["USD"]}; + STAmount const trustLimit{usd.raw(), 2, 16}; + STAmount const ownerFund{usd.raw(), Number{9'999'999'999'999'999LL}}; + + env(trust(owner, trustLimit)); + env.close(); + env(pay(issuer, owner, ownerFund)); + env.close(); + + Vault const vault{env}; + auto [vaultTx, vaultKeylet] = vault.create({.owner = owner, .asset = usd}); + vaultTx[sfScale] = 0; + env(vaultTx); + env.close(); + env(vault.deposit({.depositor = owner, .id = vaultKeylet.key, .amount = ownerFund})); + env.close(); + + // Vault pseudo-account is now at 9.999e15. Issuer mints 2 + // more USD. Pre: tecINVARIANT_FAILED at finalize. Post: + // tecPRECISION_LOSS proactively. Either way, no value moves. + env(vault.deposit({.depositor = issuer, .id = vaultKeylet.key, .amount = usd(2)}), + Ter(expected)); + env.close(); + }; + + { + testcase( + "bug: VaultDeposit by issuer at IOU edge fires " + "tecINVARIANT_FAILED at finalize (pre-fixCleanup3_2_0)"); + runScenario(testableAmendments() - fixCleanup3_2_0, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultDeposit by issuer at IOU edge rejects with " + "tecPRECISION_LOSS proactively (post-fixCleanup3_2_0)"); + runScenario(testableAmendments(), tecPRECISION_LOSS); + } + } + void testReferenceHolding() { @@ -6940,6 +7423,12 @@ public: void run() override { + testVaultWithdrawEqualityEnforced(); + testBugIssuerVaultDepositAtEdge(); + testBugMakeDeltaPosteriorScale(); + testBugMakeDeltaAnteriorScale(); + testVaultDepositCanonicalizeToZero(); + testVaultWithdrawCanonicalizeToZero(); testVaultDepositNegativeBalanceFromOppositeLimit(); testSequences(); testPreflight(); diff --git a/src/test/protocol/STAmount_test.cpp b/src/test/protocol/STAmount_test.cpp index c7207589ff..720fafa8f2 100644 --- a/src/test/protocol/STAmount_test.cpp +++ b/src/test/protocol/STAmount_test.cpp @@ -1203,8 +1203,6 @@ public: } } - //-------------------------------------------------------------------------- - void testIsZeroAtScale() { From 49567e728311299dc7a264d30b0245d70d8f6e0a Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Tue, 26 May 2026 20:18:40 +0200 Subject: [PATCH 05/28] fix: Fix edge-case where vault-depositor may get stuck (#7139) --- include/xrpl/ledger/helpers/VaultHelpers.h | 35 +- src/libxrpl/ledger/helpers/VaultHelpers.cpp | 37 +- .../tx/transactors/vault/VaultWithdraw.cpp | 89 ++- src/test/app/Vault_test.cpp | 608 ++++++++++++++++++ 4 files changed, 753 insertions(+), 16 deletions(-) diff --git a/include/xrpl/ledger/helpers/VaultHelpers.h b/include/xrpl/ledger/helpers/VaultHelpers.h index 14b0c004cb..29270d913f 100644 --- a/include/xrpl/ledger/helpers/VaultHelpers.h +++ b/include/xrpl/ledger/helpers/VaultHelpers.h @@ -1,5 +1,7 @@ #pragma once +#include +#include #include #include @@ -43,6 +45,14 @@ sharesToAssetsDeposit( /** Controls whether to truncate shares instead of rounding. */ enum class TruncateShares : bool { No = false, Yes = true }; +/** Controls whether the withdraw conversion helpers + (assetsToSharesWithdraw and sharesToAssetsWithdraw) subtract + sfLossUnrealized from sfAssetsTotal before computing the exchange rate. + The default (No) applies the standard discounted rate; Yes is used when + the redeemer is the sole remaining shareholder. +*/ +enum class WaiveUnrealizedLoss : bool { No = false, Yes = true }; + /** From the perspective of a vault, return the number of shares to demand from the depositor when they ask to withdraw a fixed amount of assets. Since shares are MPT this number is integral, and it will be rounded to nearest @@ -52,6 +62,8 @@ enum class TruncateShares : bool { No = false, Yes = true }; @param issuance The MPTokenIssuance SLE for the vault's shares. @param assets The amount of assets to convert. @param truncate Whether to truncate instead of rounding. + @param waive Whether to waive the unrealized-loss discount when computing + the exchange rate. @return The number of shares, or nullopt on error. */ @@ -60,7 +72,8 @@ assetsToSharesWithdraw( std::shared_ptr const& vault, std::shared_ptr const& issuance, STAmount const& assets, - TruncateShares truncate = TruncateShares::No); + TruncateShares truncate = TruncateShares::No, + WaiveUnrealizedLoss waive = WaiveUnrealizedLoss::No); /** From the perspective of a vault, return the number of assets to give the depositor when they redeem a fixed amount of shares. Note, since shares are @@ -69,6 +82,8 @@ assetsToSharesWithdraw( @param vault The vault SLE. @param issuance The MPTokenIssuance SLE for the vault's shares. @param shares The amount of shares to convert. + @param waive Whether to waive (i.e. not subtract) the vault's unrealized + loss when computing the exchange rate. @return The number of assets, or nullopt on error. */ @@ -76,6 +91,22 @@ assetsToSharesWithdraw( sharesToAssetsWithdraw( std::shared_ptr const& vault, std::shared_ptr const& issuance, - STAmount const& shares); + STAmount const& shares, + WaiveUnrealizedLoss waive = WaiveUnrealizedLoss::No); + +/** Returns true iff `account` holds all of the vault's outstanding shares — + i.e. is the sole remaining shareholder. Returns false if the account + holds no shares or fewer than the total outstanding. + + @param view The ledger view. + @param account The candidate sole shareholder. + @param issuance The MPTokenIssuance SLE for the vault's shares; provides + both the share MPTID and the outstanding-amount total. +*/ +[[nodiscard]] bool +isSoleShareholder( + ReadView const& view, + AccountID const& account, + std::shared_ptr const& issuance); } // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/VaultHelpers.cpp b/src/libxrpl/ledger/helpers/VaultHelpers.cpp index 8832e0078f..587923953d 100644 --- a/src/libxrpl/ledger/helpers/VaultHelpers.cpp +++ b/src/libxrpl/ledger/helpers/VaultHelpers.cpp @@ -2,11 +2,16 @@ #include #include +#include +#include +#include +#include #include #include #include #include // IWYU pragma: keep +#include #include #include @@ -70,7 +75,8 @@ assetsToSharesWithdraw( std::shared_ptr const& vault, std::shared_ptr const& issuance, STAmount const& assets, - TruncateShares truncate) + TruncateShares truncate, + WaiveUnrealizedLoss waive) { XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesWithdraw : non-negative assets"); XRPL_ASSERT( @@ -80,7 +86,8 @@ assetsToSharesWithdraw( return std::nullopt; // LCOV_EXCL_LINE Number assetTotal = vault->at(sfAssetsTotal); - assetTotal -= vault->at(sfLossUnrealized); + if (waive == WaiveUnrealizedLoss::No) + assetTotal -= vault->at(sfLossUnrealized); STAmount shares{vault->at(sfShareMPTID)}; if (assetTotal == 0) return shares; @@ -96,7 +103,8 @@ assetsToSharesWithdraw( sharesToAssetsWithdraw( std::shared_ptr const& vault, std::shared_ptr const& issuance, - STAmount const& shares) + STAmount const& shares, + WaiveUnrealizedLoss waive) { XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsWithdraw : non-negative shares"); XRPL_ASSERT( @@ -106,7 +114,8 @@ sharesToAssetsWithdraw( return std::nullopt; // LCOV_EXCL_LINE Number assetTotal = vault->at(sfAssetsTotal); - assetTotal -= vault->at(sfLossUnrealized); + if (waive == WaiveUnrealizedLoss::No) + assetTotal -= vault->at(sfLossUnrealized); STAmount assets{vault->at(sfAsset)}; if (assetTotal == 0) return assets; @@ -115,4 +124,24 @@ sharesToAssetsWithdraw( return assets; } +[[nodiscard]] bool +isSoleShareholder(ReadView const& view, AccountID const& account, SLE::const_ref issuance) +{ + XRPL_ASSERT( + issuance && issuance->getType() == ltMPTOKEN_ISSUANCE, + "xrpl::isSoleShareholder : valid issuance SLE"); + + std::uint64_t const outstanding = issuance->at(sfOutstandingAmount); + if (outstanding == 0) + return false; + + auto const shareMPTID = + makeMptID(issuance->getFieldU32(sfSequence), issuance->getAccountID(sfIssuer)); + auto const sleToken = view.read(keylet::mptoken(shareMPTID, account)); + if (!sleToken) + return false; // LCOV_EXCL_LINE + + return sleToken->getFieldU64(sfMPTAmount) == outstanding; +} + } // namespace xrpl diff --git a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp index ced82f6735..c52d94ac0b 100644 --- a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp @@ -4,12 +4,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -26,6 +28,18 @@ namespace xrpl { +static WaiveUnrealizedLoss +shouldWaiveWithdrawal(ReadView const& view, AccountID const& account, SLE::const_ref issuance) +{ + XRPL_ASSERT( + issuance && issuance->getType() == ltMPTOKEN_ISSUANCE, + "xrpl::shouldWaiveWithdrawal : valid issuance sle"); + + return view.rules().enabled(fixCleanup3_2_0) && isSoleShareholder(view, account, issuance) + ? WaiveUnrealizedLoss::Yes + : WaiveUnrealizedLoss::No; +} + NotTEC VaultWithdraw::preflight(PreflightContext const& ctx) { @@ -102,9 +116,14 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx) // LCOV_EXCL_STOP } + // When the user is the sole shareholder they own both the available and future value. + // We waive the unrealized-loss subtraction in this case to avoid user withdrawing all of + // their shares but keeping future value in the vault. + auto const waiveUnrealizedLoss = shouldWaiveWithdrawal(ctx.view, account, sleIssuance); try { - auto const maybeAssets = sharesToAssetsWithdraw(vault, sleIssuance, amount); + auto const maybeAssets = + sharesToAssetsWithdraw(vault, sleIssuance, amount, waiveUnrealizedLoss); if (!maybeAssets) return tefINTERNAL; // LCOV_EXCL_LINE @@ -182,13 +201,19 @@ VaultWithdraw::doApply() MPTIssue const share{mptIssuanceID}; STAmount sharesRedeemed = {share}; STAmount assetsWithdrawn; + + // When the user is the sole shareholder they own both the available and future value. + // We waive the unrealized-loss subtraction in this case to avoid user withdrawing all of their + // shares but keeping future value in the vault. + auto const waiveUnrealizedLoss = shouldWaiveWithdrawal(view(), accountID_, sleIssuance); try { if (amount.asset() == vaultAsset) { // Fixed assets, variable shares. { - auto const maybeShares = assetsToSharesWithdraw(vault, sleIssuance, amount); + auto const maybeShares = assetsToSharesWithdraw( + vault, sleIssuance, amount, TruncateShares::No, waiveUnrealizedLoss); if (!maybeShares) return tecINTERNAL; // LCOV_EXCL_LINE sharesRedeemed = *maybeShares; @@ -196,7 +221,8 @@ VaultWithdraw::doApply() if (sharesRedeemed == beast::kZero) return tecPRECISION_LOSS; - auto const maybeAssets = sharesToAssetsWithdraw(vault, sleIssuance, sharesRedeemed); + auto const maybeAssets = + sharesToAssetsWithdraw(vault, sleIssuance, sharesRedeemed, waiveUnrealizedLoss); if (!maybeAssets) return tecINTERNAL; // LCOV_EXCL_LINE assetsWithdrawn = *maybeAssets; @@ -205,7 +231,8 @@ VaultWithdraw::doApply() { // Fixed shares, variable assets. sharesRedeemed = amount; - auto const maybeAssets = sharesToAssetsWithdraw(vault, sleIssuance, sharesRedeemed); + auto const maybeAssets = + sharesToAssetsWithdraw(vault, sleIssuance, sharesRedeemed, waiveUnrealizedLoss); if (!maybeAssets) return tecINTERNAL; // LCOV_EXCL_LINE assetsWithdrawn = *maybeAssets; @@ -238,22 +265,64 @@ VaultWithdraw::doApply() auto assetsAvailable = vault->at(sfAssetsAvailable); auto assetsTotal = vault->at(sfAssetsTotal); - [[maybe_unused]] auto const lossUnrealized = vault->at(sfLossUnrealized); + auto const lossUnrealized = vault->at(sfLossUnrealized); XRPL_ASSERT( lossUnrealized <= (assetsTotal - assetsAvailable), "xrpl::VaultWithdraw::doApply : loss and assets do balance"); - // The vault must have enough assets on hand. The vault may hold assets - // that it has already pledged. That is why we look at AssetAvailable - // instead of the pseudo-account balance. + // The vault must have enough assets on hand. if (*assetsAvailable < assetsWithdrawn) { JLOG(j_.debug()) << "VaultWithdraw: vault doesn't hold enough assets"; return tecINSUFFICIENT_FUNDS; } - assetsTotal -= assetsWithdrawn; - assetsAvailable -= assetsWithdrawn; + // Post-fixCleanup3_2_0 "final withdrawal" rule: + // a transaction that would burn every outstanding share is only permitted when the vault is in + // a clean state — no outstanding receivables and no unrealized loss. Otherwise the resulting + // (shares == 0, assetsTotal > 0) state would violate the zero-sized-vault invariant. + // + // When the rule applies, the payout is the remaining sfAssetsAvailable; in a clean vault + // the helper result should already equal that value, and any mismatch is a rounding artifact + // worth logging. + bool const isFinalWithdrawal = + sharesRedeemed == STAmount{share, sleIssuance->at(sfOutstandingAmount)}; + if (view().rules().enabled(fixCleanup3_2_0) && isFinalWithdrawal) + { + // Unreachable: a final withdrawal with lossUnrealized > 0 has + // assetsWithdrawn == assetsTotal > assetsAvailable, which the + // insufficient-funds guard above already rejected. + if (*lossUnrealized != beast::kZero) + { + // LCOV_EXCL_START + UNREACHABLE( + "xrpl::VaultWithdraw::doApply : final withdrawal with non-zero unrealized loss"); + JLOG(j_.fatal()) + << "VaultWithdraw: " // + "Cannot burn all outstanding shares while unrealized loss is non-zero"; + return tefINTERNAL; + // LCOV_EXCL_END + } + + STAmount const allAvailable{vaultAsset, *assetsAvailable}; + if (assetsWithdrawn != allAvailable) + { + JLOG(j_.error()) // + << "VaultWithdraw: final withdrawal share-value mismatch;" + << " computed=" << assetsWithdrawn.getText() + << " assetsAvailable=" << allAvailable.getText(); + } + assetsWithdrawn = allAvailable; + + // Do not let dust accumulate in the Vault. + assetsTotal = 0; + assetsAvailable = 0; + } + else + { + assetsTotal -= assetsWithdrawn; + assetsAvailable -= assetsWithdrawn; + } view().update(vault); auto const& vaultAccount = vault->at(sfAccount); diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index d8527876f1..bf707afae9 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -6457,6 +6457,604 @@ class Vault_test : public beast::unit_test::Suite runTest(amendments); } + // ----------------------------------------------------------------------- + // Helpers and tests: sole-shareholder / stuck-depositor (XLS-0065 + + // fixCleanup3_2_0). The vault-level withdraw behavior is tested here; + // the loan-protocol setup is incidental. + // ----------------------------------------------------------------------- + + FeatureBitset const all_{test::jtx::testableAmendments()}; + std::string const iouCurrency_{"IOU"}; + + // design doc: + // AssetsAvailable ≈ 3,333.50 + // AssetsTotal ≈ 6,666.50 (3,333.50 cash + 3,333 receivable) + // LossUnrealized = 3,333 + // OutstandingShares = sharesLender (5e9 at IOU scale 1e6) + struct StuckDepositorFixture + { + test::jtx::Account issuer{"issuer"}; + test::jtx::Account lender{"lender"}; + test::jtx::Account bob{"bob"}; + test::jtx::Account borrower{"borrower"}; + std::optional asset; + std::optional vaultKeylet; + uint256 brokerID; + std::optional loanKeylet; + MPTID shareAsset; + std::uint64_t sharesLender = 0; + }; + + static constexpr std::int64_t kStuckFunding = 1'000'000; + static constexpr std::int64_t kStuckDepositorIOU = 1'000'000; + static constexpr std::int64_t kStuckBorrowerIOU = 100'000; + static constexpr std::int64_t kStuckDeposit = 5'000; + static constexpr std::int64_t kStuckPrincipal = 3'333; + static constexpr std::uint32_t kStuckPayInterval = 600; + static constexpr std::uint32_t kStuckPayTotal = 2; + + [[nodiscard]] StuckDepositorFixture + setupStuckDepositor(test::jtx::Env& env) + { + using namespace test::jtx; + + StuckDepositorFixture f; + f.asset = f.issuer[iouCurrency_]; + + env.fund(XRP(kStuckFunding), f.issuer, f.lender, f.bob, f.borrower); + env.close(); + + env(trust(f.lender, (*f.asset)(10'000'000))); + env(trust(f.bob, (*f.asset)(10'000'000))); + env(trust(f.borrower, (*f.asset)(10'000'000))); + env.close(); + + env(pay(f.issuer, f.lender, (*f.asset)(kStuckDepositorIOU))); + env(pay(f.issuer, f.bob, (*f.asset)(kStuckDepositorIOU))); + env(pay(f.issuer, f.borrower, (*f.asset)(kStuckBorrowerIOU))); + env.close(); + + // Vault: Lender creates and seeds it; Bob matches the deposit for a + // clean 50/50 split. + Vault const v{env}; + auto [createTx, vaultKeylet] = v.create({.owner = f.lender, .asset = *f.asset}); + env(createTx); + env.close(); + if (!BEAST_EXPECT(env.le(vaultKeylet))) + return f; + f.vaultKeylet = vaultKeylet; + + env(v.deposit({ + .depositor = f.lender, + .id = vaultKeylet.key, + .amount = (*f.asset)(kStuckDeposit), + }), + Ter(tesSUCCESS)); + env(v.deposit({ + .depositor = f.bob, + .id = vaultKeylet.key, + .amount = (*f.asset)(kStuckDeposit), + }), + Ter(tesSUCCESS)); + env.close(); + + // Loan broker: no cover, no management fee, debt cap 10x principal. + f.brokerID = keylet::loanbroker(f.lender.id(), env.seq(f.lender)).key; + { + using namespace loanBroker; + env(set(f.lender, vaultKeylet.key), + kDebtMaximum((*f.asset)(kStuckPrincipal * 10).value())); + env.close(); + } + + // Loan: 3,333 USD principal, impaired immediately. + auto const sleBroker = env.le(keylet::loanbroker(f.brokerID)); + if (!BEAST_EXPECT(sleBroker)) + return f; + f.loanKeylet = keylet::loan(f.brokerID, sleBroker->at(sfLoanSequence)); + + { + using namespace loan; + env(set(f.borrower, f.brokerID, kStuckPrincipal), + Sig(sfCounterpartySignature, f.lender), + kPaymentTotal(kStuckPayTotal), + kPaymentInterval(kStuckPayInterval), + Fee(env.current()->fees().base * 2), + Ter(tesSUCCESS)); + env.close(); + env(manage(f.lender, f.loanKeylet->key, tfLoanImpair), Ter(tesSUCCESS)); + env.close(); + } + + auto const vaultSle = env.le(vaultKeylet); + if (!BEAST_EXPECT(vaultSle)) + return f; + BEAST_EXPECT(vaultSle->at(sfLossUnrealized) == (*f.asset)(kStuckPrincipal).value()); + + f.shareAsset = vaultSle->at(sfShareMPTID); + + auto const tokenBob = env.le(keylet::mptoken(f.shareAsset, f.bob.id())); + if (!BEAST_EXPECT(tokenBob)) + return f; + std::uint64_t const sharesBob = tokenBob->getFieldU64(sfMPTAmount); + + // Bob (non-sole) exits at the discounted rate. Always succeeds. + STAmount const bobShareAmt{MPTIssue{f.shareAsset}, Number(sharesBob)}; + env(v.withdraw({ + .depositor = f.bob, + .id = vaultKeylet.key, + .amount = bobShareAmt, + }), + Ter(tesSUCCESS)); + env.close(); + + auto const tokenLender = env.le(keylet::mptoken(f.shareAsset, f.lender.id())); + if (!BEAST_EXPECT(tokenLender)) + return f; + f.sharesLender = tokenLender->getFieldU64(sfMPTAmount); + + auto const sleIssuance = env.le(keylet::mptIssuance(f.shareAsset)); + if (!BEAST_EXPECT(sleIssuance)) + return f; + BEAST_EXPECT(sleIssuance->getFieldU64(sfOutstandingAmount) == f.sharesLender); + + auto const vaultAfterBob = env.le(vaultKeylet); + if (!BEAST_EXPECT(vaultAfterBob)) + return f; + // After Bob's exit: loss is unchanged (3,333 receivable), and the + // gap between assetsTotal and assetsAvailable equals exactly that + // receivable. + BEAST_EXPECT(vaultAfterBob->at(sfLossUnrealized) == (*f.asset)(kStuckPrincipal).value()); + BEAST_EXPECT( + vaultAfterBob->at(sfAssetsTotal) - vaultAfterBob->at(sfAssetsAvailable) == + vaultAfterBob->at(sfLossUnrealized)); + + return f; + } + + // Reproduces the worked example from the XLS-0065 design doc. The sole + // remaining shareholder asks (via fixed-asset input) for the vault's + // entire AssetsAvailable. Pre-fix this fails with the zero-sized-vault + // invariant violation. Post-fix the full-price exchange rate burns + // only a portion of the shares, the depositor receives all of + // AssetsAvailable, and the residual shares remain backed by the + // impaired-loan receivable. + void + testWithdrawSoleShareholderFixedAssetExit(FeatureBitset features) + { + using namespace test::jtx; + + bool const withFix = features[fixCleanup3_2_0]; + testcase( + std::string{"Vault withdraw: sole shareholder exits via " + "fixed-asset amount with impaired loan"} + + (withFix ? " (fixCleanup3_2_0)" : " (pre-fix)")); + + Env env(*this, features); + auto const f = setupStuckDepositor(env); + if (!f.vaultKeylet || !f.asset || f.sharesLender == 0) + { + BEAST_EXPECT(false); + return; + } + Keylet const& vaultKey = *f.vaultKeylet; + PrettyAsset const& asset = *f.asset; + + auto const vaultBefore = env.le(vaultKey); + if (!BEAST_EXPECT(vaultBefore)) + return; + Number const availableBefore = vaultBefore->at(sfAssetsAvailable); + Number const totalBefore = vaultBefore->at(sfAssetsTotal); + Number const lossBefore = vaultBefore->at(sfLossUnrealized); + + STAmount const lenderBalanceBefore = env.balance(f.lender, asset); + + // The requested amount differs between feature regimes because + // the two regimes are testing different behaviors: + // + // - Pre-fix: request the full AssetsAvailable (3,333.50). Under + // the discounted formula this would burn every outstanding + // share, hitting the zero-sized-vault invariant. The + // transaction is rejected with tecINVARIANT_FAILED — the + // stuck-depositor bug. + // + // - Post-fix: request a strictly smaller amount (1,000 USD). + // The full-price formula burns only ~30% of the outstanding + // shares; the vault retains the rest, backed by the impaired + // receivable. Requesting *exactly* AssetsAvailable post-fix + // would currently fail with tecINSUFFICIENT_FUNDS due to the + // round-to-nearest used by assetsToSharesWithdraw (the + // recomputed payout can overshoot the request by a few ULPs). + // The "force payout to AssetsAvailable" branch in doApply + // only triggers when every share is burned, which is covered + // by the loan-repayment test. + STAmount const requestAssets = + withFix ? asset(1000).value() : STAmount{asset.raw(), availableBefore}; + Vault const v{env}; + env(v.withdraw({ + .depositor = f.lender, + .id = vaultKey.key, + .amount = requestAssets, + }), + Ter(withFix ? TER{tesSUCCESS} : TER{tecINVARIANT_FAILED})); + env.close(); + + auto const vaultAfter = env.le(vaultKey); + if (!BEAST_EXPECT(vaultAfter)) + return; + auto const issuanceAfter = env.le(keylet::mptIssuance(f.shareAsset)); + if (!BEAST_EXPECT(issuanceAfter)) + return; + + std::uint64_t const sharesAfter = issuanceAfter->getFieldU64(sfOutstandingAmount); + Number const availableAfter = vaultAfter->at(sfAssetsAvailable); + Number const totalAfter = vaultAfter->at(sfAssetsTotal); + Number const lossAfter = vaultAfter->at(sfLossUnrealized); + + if (!withFix) + { + // Pre-fix: rejected — vault state unchanged. + BEAST_EXPECT(sharesAfter == f.sharesLender); + BEAST_EXPECT(availableAfter == availableBefore); + BEAST_EXPECT(totalAfter == totalBefore); + BEAST_EXPECT(lossAfter == lossBefore); + return; + } + + // Post-fix exact-value derivation (fixture: sharesLender=5e9, + // totalBefore=6666.5, request=1000): + // sharesRedeemed = round(sharesLender * request / totalBefore) + // = round(750,018,750.469) = 750,018,750 + // received = totalBefore * sharesRedeemed / sharesLender + // = 999.999999375 (slightly under 1,000 due to + // integer-share rounding) + constexpr std::uint64_t kExpectedSharesRedeemed = 750'018'750; + Number const expectedReceived = + totalBefore * Number(kExpectedSharesRedeemed) / Number(f.sharesLender); + + BEAST_EXPECT(sharesAfter == f.sharesLender - kExpectedSharesRedeemed); + + // LossUnrealized is unchanged: the loan-protocol side is untouched. + BEAST_EXPECT(lossAfter == lossBefore); + + // The entire (total - available) gap is the impaired receivable, + // i.e. equal to lossUnrealized. + BEAST_EXPECT(totalAfter - availableAfter == lossAfter); + + STAmount const lenderBalanceAfter = env.balance(f.lender, asset); + Number const received{lenderBalanceAfter - lenderBalanceBefore}; + BEAST_EXPECT(received == expectedReceived); + + // Conservation: assets removed from the vault equal what the + // depositor received. + BEAST_EXPECT(totalBefore - totalAfter == received); + BEAST_EXPECT(availableBefore - availableAfter == received); + } + + // Sole shareholder attempts to burn ALL outstanding shares via + // fixed-shares input while the vault still holds an impaired + // receivable. Pre-fix this fails with the zero-sized-vault invariant + // violation. Post-fix the full-price rate causes assetsWithdrawn to + // equal assetsTotal, which exceeds assetsAvailable, so the transaction + // is rejected with tecINSUFFICIENT_FUNDS. + void + testWithdrawSoleShareholderFullSharesRejected(FeatureBitset features) + { + using namespace test::jtx; + + bool const withFix = features[fixCleanup3_2_0]; + testcase( + std::string{"Vault withdraw: sole shareholder full-shares " + "burn is rejected while loss outstanding"} + + (withFix ? " (fixCleanup3_2_0)" : " (pre-fix)")); + + Env env(*this, features); + auto const f = setupStuckDepositor(env); + if (!f.vaultKeylet || f.sharesLender == 0) + { + BEAST_EXPECT(false); + return; + } + Keylet const& vaultKey = *f.vaultKeylet; + + auto const vaultBefore = env.le(vaultKey); + if (!BEAST_EXPECT(vaultBefore)) + return; + Number const availableBefore = vaultBefore->at(sfAssetsAvailable); + Number const totalBefore = vaultBefore->at(sfAssetsTotal); + Number const lossBefore = vaultBefore->at(sfLossUnrealized); + + // Fixed-shares input: ask for ALL outstanding shares. + STAmount const shareAmt{MPTIssue{f.shareAsset}, Number(f.sharesLender)}; + Vault const v{env}; + env(v.withdraw({ + .depositor = f.lender, + .id = vaultKey.key, + .amount = shareAmt, + }), + Ter(withFix ? TER{tecINSUFFICIENT_FUNDS} : TER{tecINVARIANT_FAILED})); + env.close(); + + // Either way the transaction was rejected; vault state unchanged. + auto const vaultAfter = env.le(vaultKey); + if (!BEAST_EXPECT(vaultAfter)) + return; + auto const issuanceAfter = env.le(keylet::mptIssuance(f.shareAsset)); + if (!BEAST_EXPECT(issuanceAfter)) + return; + BEAST_EXPECT(issuanceAfter->getFieldU64(sfOutstandingAmount) == f.sharesLender); + BEAST_EXPECT(vaultAfter->at(sfAssetsAvailable) == availableBefore); + BEAST_EXPECT(vaultAfter->at(sfAssetsTotal) == totalBefore); + BEAST_EXPECT(vaultAfter->at(sfLossUnrealized) == lossBefore); + } + + // Post-fix end-to-end resolution: after the sole-shareholder partial + // exit, the loan is repaid in full. With unrealized loss cleared and + // all assets back as cash, the depositor can burn all remaining + // shares and fully exit the vault. The final withdrawal hits the + // "force payout to assetsAvailable" branch in doApply. + void + testWithdrawSoleShareholderLoanRepaymentExit() + { + using namespace test::jtx; + using namespace loan; + + testcase( + "Vault withdraw: sole shareholder fully exits after impaired " + "loan is repaid (fixCleanup3_2_0)"); + + Env env(*this, all_ | fixCleanup3_2_0); + auto const f = setupStuckDepositor(env); + if (!f.vaultKeylet || !f.asset || !f.loanKeylet || f.sharesLender == 0) + { + BEAST_EXPECT(false); + return; + } + Keylet const& vaultKey = *f.vaultKeylet; + Keylet const& loanKey = *f.loanKeylet; + PrettyAsset const& asset = *f.asset; + + Vault const v{env}; + + // Sole-shareholder partial exit (see comment in + // testWithdrawSoleShareholderFixedAssetExit for why we request + // less than full AssetsAvailable). + { + STAmount const requestAssets = asset(1000).value(); + env(v.withdraw({ + .depositor = f.lender, + .id = vaultKey.key, + .amount = requestAssets, + }), + Ter(tesSUCCESS)); + env.close(); + } + + // Confirm the "dormant-but-alive" state from the design doc. The + // partial exit burned exactly 750,018,750 shares (see derivation + // in testWithdrawSoleShareholderFixedAssetExit). + auto const tokenAfterExit = env.le(keylet::mptoken(f.shareAsset, f.lender.id())); + if (!BEAST_EXPECT(tokenAfterExit)) + return; + std::uint64_t const retainedShares = tokenAfterExit->getFieldU64(sfMPTAmount); + BEAST_EXPECT(retainedShares == f.sharesLender - 750'018'750); + + // Borrower repays the loan in full (pays more than the outstanding + // total; the loan transactor caps the receivable). + env(pay(f.borrower, loanKey.key, asset(kStuckPrincipal * 2)), Ter(tesSUCCESS)); + env.close(); + + auto const vaultAfterRepay = env.le(vaultKey); + if (!BEAST_EXPECT(vaultAfterRepay)) + return; + // Repayment converts the 3,333 receivable back to cash; assetsTotal + // is unchanged but assetsAvailable jumps by exactly the same amount, + // and lossUnrealized clears to zero. + BEAST_EXPECT(vaultAfterRepay->at(sfLossUnrealized) == beast::kZero); + BEAST_EXPECT(vaultAfterRepay->at(sfAssetsAvailable) == vaultAfterRepay->at(sfAssetsTotal)); + + STAmount const lenderBalanceBeforeFinal = env.balance(f.lender, asset); + Number const availableBeforeFinal = vaultAfterRepay->at(sfAssetsAvailable); + + // Burn all remaining shares — the clean-state preconditions of + // the "final withdrawal" guard are now satisfied. + STAmount const allShares{MPTIssue{f.shareAsset}, Number(retainedShares)}; + env(v.withdraw({ + .depositor = f.lender, + .id = vaultKey.key, + .amount = allShares, + }), + Ter(tesSUCCESS)); + env.close(); + + auto const vaultFinal = env.le(vaultKey); + if (!BEAST_EXPECT(vaultFinal)) + return; + auto const issuanceFinal = env.le(keylet::mptIssuance(f.shareAsset)); + if (!BEAST_EXPECT(issuanceFinal)) + return; + + // Zero-sized vault invariant satisfied: 0 shares, 0 assets. + BEAST_EXPECT(issuanceFinal->getFieldU64(sfOutstandingAmount) == 0); + BEAST_EXPECT(vaultFinal->at(sfAssetsTotal) == beast::kZero); + BEAST_EXPECT(vaultFinal->at(sfAssetsAvailable) == beast::kZero); + BEAST_EXPECT(vaultFinal->at(sfLossUnrealized) == beast::kZero); + + // The final payout equals exactly the AssetsAvailable that + // existed before the call (the "force payout" branch). + STAmount const lenderBalanceAfter = env.balance(f.lender, asset); + Number const finalReceived{lenderBalanceAfter - lenderBalanceBeforeFinal}; + BEAST_EXPECT(finalReceived == availableBeforeFinal); + } + + // Clean-state regression: with no impaired loan, a sole shareholder + // burning all their shares fully empties the vault under both the + // pre-fix and post-fix code paths. Confirms the new logic doesn't + // break the existing happy-path close-out. + void + testWithdrawSoleShareholderCleanVaultUnaffected(FeatureBitset features) + { + using namespace test::jtx; + + bool const withFix = features[fixCleanup3_2_0]; + testcase( + std::string{"Vault withdraw: sole shareholder clean-state " + "close-out unchanged"} + + (withFix ? " (fixCleanup3_2_0)" : " (pre-fix)")); + + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const lender{"lender"}; + + env.fund(XRP(kStuckFunding), issuer, lender); + env.close(); + + PrettyAsset const asset = issuer[iouCurrency_]; + env(trust(lender, asset(10'000'000))); + env.close(); + env(pay(issuer, lender, asset(kStuckDepositorIOU))); + env.close(); + + // Sole shareholder of a clean vault — no loan broker needed. + Vault const v{env}; + auto [createTx, vaultKeylet] = v.create({.owner = lender, .asset = asset}); + env(createTx); + env.close(); + + env(v.deposit({ + .depositor = lender, + .id = vaultKeylet.key, + .amount = asset(kStuckDeposit), + }), + Ter(tesSUCCESS)); + env.close(); + + auto const vaultBefore = env.le(vaultKeylet); + if (!BEAST_EXPECT(vaultBefore)) + return; + auto const shareAsset = vaultBefore->at(sfShareMPTID); + auto const tokenLender = env.le(keylet::mptoken(shareAsset, lender.id())); + if (!BEAST_EXPECT(tokenLender)) + return; + std::uint64_t const sharesLender = tokenLender->getFieldU64(sfMPTAmount); + + // Sole shareholder, no loans, no loss. Burn everything. + STAmount const allShares{MPTIssue{shareAsset}, Number(sharesLender)}; + env(v.withdraw({ + .depositor = lender, + .id = vaultKeylet.key, + .amount = allShares, + }), + Ter(tesSUCCESS)); + env.close(); + + auto const vaultFinal = env.le(vaultKeylet); + if (!BEAST_EXPECT(vaultFinal)) + return; + auto const issuanceFinal = env.le(keylet::mptIssuance(shareAsset)); + if (!BEAST_EXPECT(issuanceFinal)) + return; + BEAST_EXPECT(issuanceFinal->getFieldU64(sfOutstandingAmount) == 0); + BEAST_EXPECT(vaultFinal->at(sfAssetsTotal) == beast::kZero); + BEAST_EXPECT(vaultFinal->at(sfAssetsAvailable) == beast::kZero); + BEAST_EXPECT(vaultFinal->at(sfLossUnrealized) == beast::kZero); + + // (Pre-fix path takes the regular code path; post-fix path enters + // the new final-withdrawal guard, which forces payout to exactly + // assetsAvailable. Either way the result is identical for a clean + // vault.) + (void)withFix; + } + + // Sole shareholder in an impaired vault redeems a *partial* count of + // shares via fixed-shares input. Pre-fix the discounted formula is + // used; post-fix the full-price formula is used (waiveUnrealizedLoss + // = Yes). The relative payout therefore differs, and post-fix the + // depositor recovers proportionally more of the residual cash for + // the shares burned. In both cases the vault is left in a valid + // (non-empty) state. + void + testWithdrawSoleShareholderPartialFixedSharesUsesFullPrice() + { + using namespace test::jtx; + + testcase( + "Vault withdraw: sole-shareholder partial fixed-shares uses " + "full-price rate (fixCleanup3_2_0)"); + + Env env(*this, all_ | fixCleanup3_2_0); + auto const f = setupStuckDepositor(env); + if (!f.vaultKeylet || !f.asset || f.sharesLender == 0) + { + BEAST_EXPECT(false); + return; + } + Keylet const& vaultKey = *f.vaultKeylet; + PrettyAsset const& asset = *f.asset; + + auto const vaultBefore = env.le(vaultKey); + if (!BEAST_EXPECT(vaultBefore)) + return; + Number const totalBefore = vaultBefore->at(sfAssetsTotal); + Number const availableBefore = vaultBefore->at(sfAssetsAvailable); + Number const lossBefore = vaultBefore->at(sfLossUnrealized); + + // Burn exactly half of the outstanding shares. + std::uint64_t const halfShares = f.sharesLender / 2; + STAmount const halfAmt{MPTIssue{f.shareAsset}, Number(halfShares)}; + + STAmount const lenderBalanceBefore = env.balance(f.lender, asset); + + Vault const v{env}; + env(v.withdraw({ + .depositor = f.lender, + .id = vaultKey.key, + .amount = halfAmt, + }), + Ter(tesSUCCESS)); + env.close(); + + // Expected payout under the full-price formula: + // assets = totalBefore * halfShares / sharesLender + // which (with halfShares == sharesLender/2) is roughly + // totalBefore / 2. + STAmount const lenderBalanceAfter = env.balance(f.lender, asset); + Number const received{lenderBalanceAfter - lenderBalanceBefore}; + Number const expected = totalBefore * Number(halfShares) / Number(f.sharesLender); + BEAST_EXPECT(received == expected); + + // The full-price payout exceeds the discounted formula by exactly + // lossBefore * halfShares / sharesLender — that's the whole point + // of the waive. + Number const discounted = + (totalBefore - lossBefore) * Number(halfShares) / Number(f.sharesLender); + Number const expectedDelta = lossBefore * Number(halfShares) / Number(f.sharesLender); + BEAST_EXPECT(received - discounted == expectedDelta); + + auto const vaultAfter = env.le(vaultKey); + if (!BEAST_EXPECT(vaultAfter)) + return; + auto const issuanceAfter = env.le(keylet::mptIssuance(f.shareAsset)); + if (!BEAST_EXPECT(issuanceAfter)) + return; + + // Vault remains valid: half the shares remain, lossUnrealized + // is untouched, and the entire (total - available) gap is still + // the impaired receivable. + BEAST_EXPECT( + issuanceAfter->getFieldU64(sfOutstandingAmount) == f.sharesLender - halfShares); + BEAST_EXPECT(vaultAfter->at(sfAssetsTotal) == totalBefore - received); + BEAST_EXPECT(vaultAfter->at(sfLossUnrealized) == lossBefore); + BEAST_EXPECT( + vaultAfter->at(sfAssetsTotal) - vaultAfter->at(sfAssetsAvailable) == + vaultAfter->at(sfLossUnrealized)); + + // Conservation: vault delta matches the depositor's gain. + BEAST_EXPECT(totalBefore - vaultAfter->at(sfAssetsTotal) == received); + BEAST_EXPECT(availableBefore - vaultAfter->at(sfAssetsAvailable) == received); + } + // Bug: DeltaInfo::makeDelta uses max(scale(after), scale(before)) for the // sfAssetsTotal and sfAssetsAvailable deltas, and visitEntry applies the // same max() for the vault pseudo-account RippleState. When @@ -7449,6 +8047,16 @@ public: testAssetsMaximum(); testBug6LimitBypassWithShares(); testRemoveEmptyHoldingLockedAmount(); + + testWithdrawSoleShareholderFixedAssetExit(all_ - fixCleanup3_2_0); + testWithdrawSoleShareholderFixedAssetExit(all_); + testWithdrawSoleShareholderFullSharesRejected(all_ - fixCleanup3_2_0); + testWithdrawSoleShareholderFullSharesRejected(all_); + testWithdrawSoleShareholderCleanVaultUnaffected(all_ - fixCleanup3_2_0); + testWithdrawSoleShareholderCleanVaultUnaffected(all_); + testWithdrawSoleShareholderPartialFixedSharesUsesFullPrice(); + testWithdrawSoleShareholderLoanRepaymentExit(); + testReferenceHolding(); testHoldingDeletionBlocked(); } From 23d08128278a7dcd80cdbd718df8c069a8b25586 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 26 May 2026 19:28:23 +0100 Subject: [PATCH 06/28] style: Use shfmt instead of bashate (#7326) --- .github/scripts/rename/binary.sh | 2 +- .github/scripts/rename/cmake.sh | 12 ++-- .github/scripts/rename/config.sh | 2 +- .github/scripts/rename/copyright.sh | 24 ++++---- .github/scripts/rename/definitions.sh | 2 +- .github/scripts/rename/docs.sh | 2 +- .github/scripts/rename/namespace.sh | 2 +- .pre-commit-config.yaml | 20 +++---- bin/git/setup-upstreams.sh | 24 ++++---- bin/git/squash-branches.sh | 14 ++--- bin/git/update-version.sh | 20 +++---- cspell.config.yaml | 1 + docker/check-sanitizers.sh | 14 +++-- package/build_pkg.sh | 81 +++++++++++++++++---------- package/shared/update-xrpld | 4 +- 15 files changed, 127 insertions(+), 97 deletions(-) diff --git a/.github/scripts/rename/binary.sh b/.github/scripts/rename/binary.sh index cdce6db4ba..89d884538c 100755 --- a/.github/scripts/rename/binary.sh +++ b/.github/scripts/rename/binary.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi diff --git a/.github/scripts/rename/cmake.sh b/.github/scripts/rename/cmake.sh index 9c91e8f277..28bf777fed 100755 --- a/.github/scripts/rename/cmake.sh +++ b/.github/scripts/rename/cmake.sh @@ -8,12 +8,12 @@ set -e SED_COMMAND=sed HEAD_COMMAND=head if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi SED_COMMAND=gsed - if ! command -v ghead &> /dev/null; then + if ! command -v ghead &>/dev/null; then echo "Error: ghead is not installed. Please install it using 'brew install coreutils'." exit 1 fi @@ -74,10 +74,10 @@ if grep -q '"xrpld"' cmake/XrplCore.cmake; then # The script has been rerun, so just restore the name of the binary. ${SED_COMMAND} -i 's/"xrpld"/"rippled"/' cmake/XrplCore.cmake elif ! grep -q '"rippled"' cmake/XrplCore.cmake; then - ${HEAD_COMMAND} -n -1 cmake/XrplCore.cmake > cmake.tmp - echo ' # For the time being, we will keep the name of the binary as it was.' >> cmake.tmp - echo ' set_target_properties(xrpld PROPERTIES OUTPUT_NAME "rippled")' >> cmake.tmp - tail -1 cmake/XrplCore.cmake >> cmake.tmp + ${HEAD_COMMAND} -n -1 cmake/XrplCore.cmake >cmake.tmp + echo ' # For the time being, we will keep the name of the binary as it was.' >>cmake.tmp + echo ' set_target_properties(xrpld PROPERTIES OUTPUT_NAME "rippled")' >>cmake.tmp + tail -1 cmake/XrplCore.cmake >>cmake.tmp mv cmake.tmp cmake/XrplCore.cmake fi diff --git a/.github/scripts/rename/config.sh b/.github/scripts/rename/config.sh index 81edcc73d6..ac9debb154 100755 --- a/.github/scripts/rename/config.sh +++ b/.github/scripts/rename/config.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi diff --git a/.github/scripts/rename/copyright.sh b/.github/scripts/rename/copyright.sh index 9ebdad1e89..09bc5a8926 100755 --- a/.github/scripts/rename/copyright.sh +++ b/.github/scripts/rename/copyright.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi @@ -62,37 +62,37 @@ done # restoring the verbiage that is already present in LICENSE.md. Ensure that if # the script is run multiple times, duplicate notices are not added. if ! grep -q 'Raw Material Software' include/xrpl/beast/core/CurrentThreadName.h; then - echo -e "// Portions of this file are from JUCE (http://www.juce.com).\n// Copyright (c) 2013 - Raw Material Software Ltd.\n// Please visit http://www.juce.com\n\n$(cat include/xrpl/beast/core/CurrentThreadName.h)" > include/xrpl/beast/core/CurrentThreadName.h + echo -e "// Portions of this file are from JUCE (http://www.juce.com).\n// Copyright (c) 2013 - Raw Material Software Ltd.\n// Please visit http://www.juce.com\n\n$(cat include/xrpl/beast/core/CurrentThreadName.h)" >include/xrpl/beast/core/CurrentThreadName.h fi if ! grep -q 'Dev Null' src/test/app/NetworkID_test.cpp; then - echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/NetworkID_test.cpp)" > src/test/app/NetworkID_test.cpp + echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/NetworkID_test.cpp)" >src/test/app/NetworkID_test.cpp fi if ! grep -q 'Dev Null' src/test/app/tx/apply_test.cpp; then - echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/tx/apply_test.cpp)" > src/test/app/tx/apply_test.cpp + echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/tx/apply_test.cpp)" >src/test/app/tx/apply_test.cpp fi if ! grep -q 'Dev Null' src/test/rpc/ManifestRPC_test.cpp; then - echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ManifestRPC_test.cpp)" > src/test/rpc/ManifestRPC_test.cpp + echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ManifestRPC_test.cpp)" >src/test/rpc/ManifestRPC_test.cpp fi if ! grep -q 'Dev Null' src/test/rpc/ValidatorInfo_test.cpp; then - echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ValidatorInfo_test.cpp)" > src/test/rpc/ValidatorInfo_test.cpp + echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ValidatorInfo_test.cpp)" >src/test/rpc/ValidatorInfo_test.cpp fi if ! grep -q 'Dev Null' src/xrpld/rpc/handlers/server_info/Manifest.cpp; then - echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/server_info/Manifest.cpp)" > src/xrpld/rpc/handlers/server_info/Manifest.cpp + echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/server_info/Manifest.cpp)" >src/xrpld/rpc/handlers/server_info/Manifest.cpp fi if ! grep -q 'Dev Null' src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp; then - echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp)" > src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp + echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp)" >src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp fi if ! grep -q 'Bougalis' include/xrpl/basics/SlabAllocator.h; then - echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/SlabAllocator.h)" > include/xrpl/basics/SlabAllocator.h # cspell: ignore Nikolaos Bougalis nikb + echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/SlabAllocator.h)" >include/xrpl/basics/SlabAllocator.h # cspell: ignore Nikolaos Bougalis nikb fi if ! grep -q 'Bougalis' include/xrpl/basics/spinlock.h; then - echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/spinlock.h)" > include/xrpl/basics/spinlock.h # cspell: ignore Nikolaos Bougalis nikb + echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/spinlock.h)" >include/xrpl/basics/spinlock.h # cspell: ignore Nikolaos Bougalis nikb fi if ! grep -q 'Bougalis' include/xrpl/basics/tagged_integer.h; then - echo -e "// Copyright (c) 2014, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/tagged_integer.h)" > include/xrpl/basics/tagged_integer.h # cspell: ignore Nikolaos Bougalis nikb + echo -e "// Copyright (c) 2014, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/tagged_integer.h)" >include/xrpl/basics/tagged_integer.h # cspell: ignore Nikolaos Bougalis nikb fi if ! grep -q 'Ritchford' include/xrpl/beast/utility/Zero.h; then - echo -e "// Copyright (c) 2014, Tom Ritchford \n\n$(cat include/xrpl/beast/utility/Zero.h)" > include/xrpl/beast/utility/Zero.h # cspell: ignore Ritchford + echo -e "// Copyright (c) 2014, Tom Ritchford \n\n$(cat include/xrpl/beast/utility/Zero.h)" >include/xrpl/beast/utility/Zero.h # cspell: ignore Ritchford fi # Restore newlines and tabs in string literals in the affected file. diff --git a/.github/scripts/rename/definitions.sh b/.github/scripts/rename/definitions.sh index 5e004afe39..daa5d01e80 100755 --- a/.github/scripts/rename/definitions.sh +++ b/.github/scripts/rename/definitions.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi diff --git a/.github/scripts/rename/docs.sh b/.github/scripts/rename/docs.sh index 8b7a362405..9f080b06e5 100755 --- a/.github/scripts/rename/docs.sh +++ b/.github/scripts/rename/docs.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi diff --git a/.github/scripts/rename/namespace.sh b/.github/scripts/rename/namespace.sh index aba193b0cf..bb186bc8bc 100755 --- a/.github/scripts/rename/namespace.sh +++ b/.github/scripts/rename/namespace.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1313ad567c..23441c9dde 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,37 +37,37 @@ repos: exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/ - repo: https://github.com/pre-commit/mirrors-clang-format - rev: cd481d7b0bfb5c7b3090c21846317f9a8262e891 # frozen: v22.1.0 + rev: dd18dad857d6133e90bbe478f4f2f22ec0030269 # frozen: v22.1.5 hooks: - id: clang-format args: [--style=file] "types_or": [c++, c, proto] exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/ - - repo: https://github.com/BlankSpruce/gersemi - rev: 0.26.0 + - repo: https://github.com/BlankSpruce/gersemi-pre-commit + rev: faadd6a9d852369ca94f4d15b2404c967ba8cb01 # frozen: 0.27.6 hooks: - id: gersemi - repo: https://github.com/rbubley/mirrors-prettier - rev: c2bc67fe8f8f549cc489e00ba8b45aa18ee713b1 # frozen: v3.8.1 + rev: 515f543f5718ebfd6ce22e16708bb32c68ff96e1 # frozen: v3.8.3 hooks: - id: prettier args: [--end-of-line=auto] - repo: https://github.com/psf/black-pre-commit-mirror - rev: ea488cebbfd88a5f50b8bd95d5c829d0bb76feb8 # frozen: 26.1.0 + rev: 4160603246a6b365d4a2af661c6d71b0a0f50478 # frozen: 26.5.1 hooks: - id: black - - repo: https://github.com/openstack/bashate - rev: 5798d24d571676fc407e81df574c1ef57b520f23 # frozen: 2.1.1 + - repo: https://github.com/scop/pre-commit-shfmt + rev: 05c1426671b9237fb5e1444dd63aa5731bec0dfb # frozen: v3.13.1-1 hooks: - - id: bashate - args: ["--ignore=E006"] + - id: shfmt + args: [--write, --indent=4, --case-indent=true] - repo: https://github.com/streetsidesoftware/cspell-cli - rev: a42085ade523f591dca134379a595e7859986445 # frozen: v9.7.0 + rev: 4643f154907327ee0a2c7038f0296e0dd77d9776 # frozen: v10.0.0 hooks: - id: cspell # Spell check changed files exclude: | diff --git a/bin/git/setup-upstreams.sh b/bin/git/setup-upstreams.sh index 57c3f935f9..97c84f5507 100755 --- a/bin/git/setup-upstreams.sh +++ b/bin/git/setup-upstreams.sh @@ -1,8 +1,8 @@ #!/bin/bash if [[ $# -ne 1 || "$1" == "--help" || "$1" == "-h" ]]; then - name=$( basename $0 ) - cat <<- USAGE + name=$(basename $0) + cat <<-USAGE Usage: $name Where is the Github username of the upstream repo. e.g. XRPLF @@ -14,7 +14,7 @@ fi shift user="$1" # Get the origin URL. Expect it be an SSH-style URL -origin=$( git remote get-url origin ) +origin=$(git remote get-url origin) if [[ "${origin}" == "" ]]; then echo Invalid origin remote >&2 exit 1 @@ -22,11 +22,11 @@ fi # echo "Origin: ${origin}" # Parse the origin ifs_orig="${IFS}" -IFS=':' read remote originpath <<< "${origin}" +IFS=':' read remote originpath <<<"${origin}" # echo "Remote: ${remote}, Originpath: ${originpath}" -IFS='@' read sshuser server <<< "${remote}" +IFS='@' read sshuser server <<<"${remote}" # echo "SSHUser: ${sshuser}, Server: ${server}" -IFS='/' read originuser repo <<< "${originpath}" +IFS='/' read originuser repo <<<"${originpath}" # echo "Originuser: ${originuser}, Repo: ${repo}" if [[ "${sshuser}" == "" || "${server}" == "" || "${originuser}" == "" || "${repo}" == "" ]]; then echo "Can't parse origin URL: ${origin}" >&2 @@ -35,9 +35,9 @@ fi upstream="https://${server}/${user}/${repo}" upstreampush="${remote}:${user}/${repo}" upstreamgroup="upstream upstream-push" -current=$( git remote get-url upstream 2>/dev/null ) -currentpush=$( git remote get-url upstream-push 2>/dev/null ) -currentgroup=$( git config remotes.upstreams ) +current=$(git remote get-url upstream 2>/dev/null) +currentpush=$(git remote get-url upstream-push 2>/dev/null) +currentgroup=$(git config remotes.upstreams) if [[ "${current}" == "${upstream}" ]]; then echo "Upstream already set up correctly. Skip" elif [[ -n "${current}" && "${current}" != "${upstream}" && "${current}" != "${upstreampush}" ]]; then @@ -45,9 +45,9 @@ elif [[ -n "${current}" && "${current}" != "${upstream}" && "${current}" != "${u else if [[ "${current}" == "${upstreampush}" ]]; then echo "Upstream set to dangerous push URL. Update." - _run git remote rename upstream upstream-push || \ - _run git remote remove upstream - currentpush=$( git remote get-url upstream-push 2>/dev/null ) + _run git remote rename upstream upstream-push || + _run git remote remove upstream + currentpush=$(git remote get-url upstream-push 2>/dev/null) fi _run git remote add upstream "${upstream}" fi diff --git a/bin/git/squash-branches.sh b/bin/git/squash-branches.sh index eb4aefe23c..ba63f9c148 100755 --- a/bin/git/squash-branches.sh +++ b/bin/git/squash-branches.sh @@ -1,8 +1,8 @@ #!/bin/bash if [[ $# -lt 3 || "$1" == "--help" || "$1" = "-h" ]]; then - name=$( basename $0 ) - cat <<- USAGE + name=$(basename $0) + cat <<-USAGE Usage: $name workbranch base/branch user/branch [user/branch [...]] * workbranch will be created locally from base/branch @@ -16,7 +16,7 @@ fi work="$1" shift -branches=( $( echo "${@}" | sed "s/:/\//" ) ) +branches=($(echo "${@}" | sed "s/:/\//")) base="${branches[0]}" unset branches[0] @@ -24,10 +24,10 @@ set -e users=() for b in "${branches[@]}"; do - users+=( $( echo $b | cut -d/ -f1 ) ) + users+=($(echo $b | cut -d/ -f1)) done -users=( $( printf '%s\n' "${users[@]}" | sort -u ) ) +users=($(printf '%s\n' "${users[@]}" | sort -u)) git fetch --multiple upstreams "${users[@]}" git checkout -B "$work" --no-track "$base" @@ -40,7 +40,7 @@ done # Make sure the commits look right git log --show-signature "$base..HEAD" -parts=( $( echo $base | sed "s/\// /" ) ) +parts=($(echo $base | sed "s/\// /")) repo="${parts[0]}" b="${parts[1]}" push=$repo @@ -50,7 +50,7 @@ fi if [[ "$repo" == "upstream" ]]; then repo="upstreams" fi -cat << PUSH +cat </dev/null ) || true +push=$(git rev-parse --abbrev-ref --symbolic-full-name '@{push}' \ + 2>/dev/null) || true if [[ "${push}" != "" ]]; then echo "Warning: ${push} may already exist." fi -build=$( find -name BuildInfo.cpp ) -sed 's/\(^.*versionString =\).*$/\1 "'${version}'"/' ${build} > version.cpp && \ -diff "${build}" version.cpp && exit 1 || \ -mv -vi version.cpp ${build} +build=$(find -name BuildInfo.cpp) +sed 's/\(^.*versionString =\).*$/\1 "'${version}'"/' ${build} >version.cpp && + diff "${build}" version.cpp && exit 1 || + mv -vi version.cpp ${build} git diff @@ -47,7 +47,7 @@ git commit -S -m "Set version to ${version}" git log --oneline --first-parent ${base}^.. -cat << PUSH +cat <&2; exit 1 ;; + *) + echo "Unsupported arch: $(uname -m)" >&2 + exit 1 + ;; esac declare -A sanitize=( @@ -35,8 +38,11 @@ for compiler in g++ clang++; do echo "=== Run ${name}-${compiler} ===" output=$("$bin" 2>&1) || true echo "$output" - echo "$output" | grep -q "${expect[$name]}" \ - || { echo "expected '${expect[$name]}' from $bin"; exit 1; } + echo "$output" | grep -q "${expect[$name]}" || + { + echo "expected '${expect[$name]}' from $bin" + exit 1 + } rm -f "$bin" done done diff --git a/package/build_pkg.sh b/package/build_pkg.sh index adac2dc169..f2c2c63c12 100755 --- a/package/build_pkg.sh +++ b/package/build_pkg.sh @@ -36,12 +36,35 @@ SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-}" while [[ $# -gt 0 ]]; do case "$1" in - --src-dir) need_arg "$@"; SRC_DIR="$2"; shift 2 ;; - --build-dir) need_arg "$@"; BUILD_DIR="$2"; shift 2 ;; - --pkg-version) need_arg "$@"; PKG_VERSION="$2"; shift 2 ;; - --pkg-release) need_arg "$@"; PKG_RELEASE="$2"; shift 2 ;; - --source-date-epoch) need_arg "$@"; SOURCE_DATE_EPOCH="$2"; shift 2 ;; - -h|--help) usage; exit 0 ;; + --src-dir) + need_arg "$@" + SRC_DIR="$2" + shift 2 + ;; + --build-dir) + need_arg "$@" + BUILD_DIR="$2" + shift 2 + ;; + --pkg-version) + need_arg "$@" + PKG_VERSION="$2" + shift 2 + ;; + --pkg-release) + need_arg "$@" + PKG_RELEASE="$2" + shift 2 + ;; + --source-date-epoch) + need_arg "$@" + SOURCE_DATE_EPOCH="$2" + shift 2 + ;; + -h | --help) + usage + exit 0 + ;; *) echo "Unknown argument: $1" >&2 usage >&2 @@ -109,20 +132,20 @@ stage_common() { local dest="$1" mkdir -p "${dest}" - cp "${BUILD_DIR}/xrpld" "${dest}/xrpld" - cp "${SRC_DIR}/cfg/xrpld-example.cfg" "${dest}/xrpld.cfg" - cp "${SRC_DIR}/cfg/validators-example.txt" "${dest}/validators.txt" - cp "${SRC_DIR}/LICENSE.md" "${dest}/LICENSE.md" - cp "${SRC_DIR}/README.md" "${dest}/README.md" + cp "${BUILD_DIR}/xrpld" "${dest}/xrpld" + cp "${SRC_DIR}/cfg/xrpld-example.cfg" "${dest}/xrpld.cfg" + cp "${SRC_DIR}/cfg/validators-example.txt" "${dest}/validators.txt" + cp "${SRC_DIR}/LICENSE.md" "${dest}/LICENSE.md" + cp "${SRC_DIR}/README.md" "${dest}/README.md" - cp "${SHARED}/xrpld.service" "${dest}/xrpld.service" - cp "${SHARED}/xrpld.sysusers" "${dest}/xrpld.sysusers" - cp "${SHARED}/xrpld.tmpfiles" "${dest}/xrpld.tmpfiles" - cp "${SHARED}/xrpld.logrotate" "${dest}/xrpld.logrotate" - cp "${SHARED}/update-xrpld" "${dest}/update-xrpld" - cp "${SHARED}/update-xrpld.service" "${dest}/update-xrpld.service" - cp "${SHARED}/update-xrpld.timer" "${dest}/update-xrpld.timer" - cp "${SHARED}/50-xrpld.preset" "${dest}/50-xrpld.preset" + cp "${SHARED}/xrpld.service" "${dest}/xrpld.service" + cp "${SHARED}/xrpld.sysusers" "${dest}/xrpld.sysusers" + cp "${SHARED}/xrpld.tmpfiles" "${dest}/xrpld.tmpfiles" + cp "${SHARED}/xrpld.logrotate" "${dest}/xrpld.logrotate" + cp "${SHARED}/update-xrpld" "${dest}/update-xrpld" + cp "${SHARED}/update-xrpld.service" "${dest}/update-xrpld.service" + cp "${SHARED}/update-xrpld.timer" "${dest}/update-xrpld.timer" + cp "${SHARED}/50-xrpld.preset" "${dest}/50-xrpld.preset" } build_rpm() { @@ -159,12 +182,12 @@ build_deb() { cp -r "${DEBIAN_DIR}" "${staging}/debian" # Debhelper auto-discovers these only from debian/. - cp "${staging}/xrpld.service" "${staging}/debian/xrpld.service" - cp "${staging}/xrpld.sysusers" "${staging}/debian/xrpld.sysusers" - cp "${staging}/xrpld.tmpfiles" "${staging}/debian/xrpld.tmpfiles" - cp "${staging}/xrpld.logrotate" "${staging}/debian/xrpld.logrotate" + cp "${staging}/xrpld.service" "${staging}/debian/xrpld.service" + cp "${staging}/xrpld.sysusers" "${staging}/debian/xrpld.sysusers" + cp "${staging}/xrpld.tmpfiles" "${staging}/debian/xrpld.tmpfiles" + cp "${staging}/xrpld.logrotate" "${staging}/debian/xrpld.logrotate" cp "${staging}/update-xrpld.service" "${staging}/debian/xrpld.update-xrpld.service" - cp "${staging}/update-xrpld.timer" "${staging}/debian/xrpld.update-xrpld.timer" + cp "${staging}/update-xrpld.timer" "${staging}/debian/xrpld.update-xrpld.timer" # Debian '~' marks a pre-release; 3.2.0~b1 sorts before 3.2.0. local deb_full_version="${VER_BASE}${VER_SUFFIX:+~${VER_SUFFIX}}-${PKG_RELEASE}" @@ -175,12 +198,12 @@ build_deb() { # b, rc -> unstable (pre-release) local deb_distribution case "${VER_SUFFIX}" in - "") deb_distribution="stable" ;; - b0) deb_distribution="develop" ;; - *) deb_distribution="unstable" ;; + "") deb_distribution="stable" ;; + b0) deb_distribution="develop" ;; + *) deb_distribution="unstable" ;; esac - cat > "${staging}/debian/changelog" <"${staging}/debian/changelog" < Date: Tue, 26 May 2026 19:35:38 +0100 Subject: [PATCH 07/28] chore: Pin Python packages for codegen using uv (#7329) --- cmake/scripts/codegen/requirements.in | 13 +++ cmake/scripts/codegen/requirements.txt | 118 ++++++++++++++++++++++--- 2 files changed, 118 insertions(+), 13 deletions(-) create mode 100644 cmake/scripts/codegen/requirements.in diff --git a/cmake/scripts/codegen/requirements.in b/cmake/scripts/codegen/requirements.in new file mode 100644 index 0000000000..d799fd60fd --- /dev/null +++ b/cmake/scripts/codegen/requirements.in @@ -0,0 +1,13 @@ +# Python dependencies for XRP Ledger code generation scripts +# +# These packages are required to run the code generation scripts that +# parse macro files and generate C++ wrapper classes. + +# C preprocessor for Python - used to preprocess macro files +pcpp>=1.30 + +# Parser combinator library - used to parse the macro DSL +pyparsing>=3.0.0 + +# Template engine - used to generate C++ code from templates +Mako>=1.2.2 diff --git a/cmake/scripts/codegen/requirements.txt b/cmake/scripts/codegen/requirements.txt index d799fd60fd..ff37548c7b 100644 --- a/cmake/scripts/codegen/requirements.txt +++ b/cmake/scripts/codegen/requirements.txt @@ -1,13 +1,105 @@ -# Python dependencies for XRP Ledger code generation scripts -# -# These packages are required to run the code generation scripts that -# parse macro files and generate C++ wrapper classes. - -# C preprocessor for Python - used to preprocess macro files -pcpp>=1.30 - -# Parser combinator library - used to parse the macro DSL -pyparsing>=3.0.0 - -# Template engine - used to generate C++ code from templates -Mako>=1.2.2 +# This file was autogenerated by uv via the following command: +# uv pip compile requirements.in --generate-hashes --output-file requirements.txt +mako==1.3.12 \ + --hash=sha256:8f61569480282dbf557145ce441e4ba888be453c30989f879f0d652e39f53ea9 \ + --hash=sha256:9f778e93289bd410bb35daadeb4fc66d95a746f0b75777b942088b7fd7af550a + # via -r requirements.in +markupsafe==3.0.3 \ + --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ + --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ + --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ + --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ + --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ + --hash=sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c \ + --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ + --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \ + --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \ + --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \ + --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ + --hash=sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26 \ + --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \ + --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \ + --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \ + --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ + --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \ + --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ + --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \ + --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \ + --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ + --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \ + --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \ + --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ + --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \ + --hash=sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758 \ + --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \ + --hash=sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8 \ + --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \ + --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ + --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ + --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ + --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \ + --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ + --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \ + --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ + --hash=sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2 \ + --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ + --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \ + --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ + --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ + --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ + --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \ + --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \ + --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ + --hash=sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e \ + --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \ + --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \ + --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \ + --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \ + --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \ + --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ + --hash=sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b \ + --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \ + --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \ + --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \ + --hash=sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d \ + --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \ + --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \ + --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \ + --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ + --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ + --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \ + --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \ + --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \ + --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ + --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \ + --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \ + --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \ + --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \ + --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \ + --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \ + --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ + --hash=sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7 \ + --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \ + --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \ + --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \ + --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ + --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ + --hash=sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42 \ + --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ + --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ + --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ + --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ + --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ + --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \ + --hash=sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc \ + --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ + --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 + # via mako +pcpp==1.30 \ + --hash=sha256:05fe08292b6da57f385001c891a87f40d6aa7f46787b03e8ba326d20a3297c6e \ + --hash=sha256:5af9fbce55f136d7931ae915fae03c34030a3b36c496e72d9636cedc8e2543a1 + # via -r requirements.in +pyparsing==3.3.2 \ + --hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \ + --hash=sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc + # via -r requirements.in From 85af406a0ff234f6a571da46faee7551d254b6f6 Mon Sep 17 00:00:00 2001 From: Andrzej Budzanowski Date: Tue, 26 May 2026 21:35:32 +0200 Subject: [PATCH 08/28] fix: Update `clang-tidy` to include `src/tests` directory header check (#7307) --- .clang-tidy | 2 +- src/tests/libxrpl/helpers/Account.cpp | 2 +- src/tests/libxrpl/helpers/Account.h | 10 +++++----- src/tests/libxrpl/helpers/IOU.h | 3 +-- src/tests/libxrpl/helpers/TestFamily.h | 12 +++++------- src/tests/libxrpl/helpers/TestServiceRegistry.h | 10 ++++------ src/tests/libxrpl/helpers/TxTest.cpp | 2 +- src/tests/libxrpl/helpers/TxTest.h | 2 +- src/tests/libxrpl/tx/AccountSet.cpp | 14 +++++++------- 9 files changed, 26 insertions(+), 31 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index ee6bde6eba..b23d7ccbff 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -199,6 +199,6 @@ CheckOptions: readability-identifier-naming.PublicMemberSuffix: "" readability-identifier-naming.GlobalFunctionIgnoredRegexp: "^(to_string|hash_append|tuple_hash)$" -HeaderFilterRegex: '^.*/(test|xrpl|xrpld)/.*\.(h|hpp|ipp)$' +HeaderFilterRegex: '^.*/(tests?|xrpl|xrpld)/.*\.(h|hpp|ipp)$' ExcludeHeaderFilterRegex: '^.*/protocol_autogen/.*\.(h|hpp)$' WarningsAsErrors: "*" diff --git a/src/tests/libxrpl/helpers/Account.cpp b/src/tests/libxrpl/helpers/Account.cpp index 736ae0a24b..4862309c82 100644 --- a/src/tests/libxrpl/helpers/Account.cpp +++ b/src/tests/libxrpl/helpers/Account.cpp @@ -7,7 +7,7 @@ namespace xrpl::test { -Account const Account::master{"masterpassphrase"}; +Account const Account::kMaster{"masterpassphrase"}; Account::Account(std::string_view name, KeyType type) : name_(name) diff --git a/src/tests/libxrpl/helpers/Account.h b/src/tests/libxrpl/helpers/Account.h index 4e2d6e547f..a92497f2c3 100644 --- a/src/tests/libxrpl/helpers/Account.h +++ b/src/tests/libxrpl/helpers/Account.h @@ -26,7 +26,7 @@ public: * This account is created in the genesis ledger with all 100 billion XRP. * It uses the well-known seed "masterpassphrase". */ - static Account const master; + static Account const kMaster; /** * @brief Create an account from a name. @@ -39,28 +39,28 @@ public: explicit Account(std::string_view name, KeyType type = KeyType::Secp256k1); /** @brief Return the human-readable name. */ - std::string const& + [[nodiscard]] std::string const& name() const noexcept { return name_; } /** @brief Return the AccountID. */ - AccountID const& + [[nodiscard]] AccountID const& id() const noexcept { return id_; } /** @brief Return the public key. */ - PublicKey const& + [[nodiscard]] PublicKey const& pk() const noexcept { return keyPair_.first; } /** @brief Return the secret key. */ - SecretKey const& + [[nodiscard]] SecretKey const& sk() const noexcept { return keyPair_.second; diff --git a/src/tests/libxrpl/helpers/IOU.h b/src/tests/libxrpl/helpers/IOU.h index 18bc69cf33..d80f962edf 100644 --- a/src/tests/libxrpl/helpers/IOU.h +++ b/src/tests/libxrpl/helpers/IOU.h @@ -49,8 +49,7 @@ public: * @param currency The Currency object. * @param issuer The account that issues this currency. */ - IOU(Currency currency, Account const& issuer) - : currency_(std::move(currency)), issuer_(issuer.id()) + IOU(Currency currency, Account const& issuer) : currency_(currency), issuer_(issuer.id()) { XRPL_ASSERT(!isXRP(currency_), "IOU: currency code must not resolve to XRP"); } diff --git a/src/tests/libxrpl/helpers/TestFamily.h b/src/tests/libxrpl/helpers/TestFamily.h index 2f69a26faf..dea7a6d4b4 100644 --- a/src/tests/libxrpl/helpers/TestFamily.h +++ b/src/tests/libxrpl/helpers/TestFamily.h @@ -7,8 +7,7 @@ #include -namespace xrpl { -namespace test { +namespace xrpl::test { /** Test implementation of Family for unit tests. @@ -49,7 +48,7 @@ public: return *db_; } - NodeStore::Database const& + [[nodiscard]] NodeStore::Database const& db() const override { return *db_; @@ -95,8 +94,8 @@ public: void reset() override { - fbCache_->reset(); - tnCache_->reset(); + (*fbCache_).reset(); + (*tnCache_).reset(); } /** Access the test clock for time manipulation in tests. */ @@ -107,5 +106,4 @@ public: } }; -} // namespace test -} // namespace xrpl +} // namespace xrpl::test diff --git a/src/tests/libxrpl/helpers/TestServiceRegistry.h b/src/tests/libxrpl/helpers/TestServiceRegistry.h index 7070927842..0536176344 100644 --- a/src/tests/libxrpl/helpers/TestServiceRegistry.h +++ b/src/tests/libxrpl/helpers/TestServiceRegistry.h @@ -16,8 +16,7 @@ #include #include -namespace xrpl { -namespace test { +namespace xrpl::test { /** Logs implementation that creates TestSink instances. */ class TestLogs : public Logs @@ -63,7 +62,7 @@ private: class TestServiceRegistry : public ServiceRegistry { TestLogs logs_{beast::Severity::Warning}; - boost::asio::io_context io_context_; + boost::asio::io_context ioContext_; TestFamily family_{logs_.journal("TestFamily")}; LoadFeeTrack feeTrack_{logs_.journal("LoadFeeTrack")}; TestNetworkIDService networkIDService_; @@ -344,7 +343,7 @@ public: boost::asio::io_context& getIOContext() override { - return io_context_; + return ioContext_; } Logs& @@ -374,5 +373,4 @@ public: } }; -} // namespace test -} // namespace xrpl +} // namespace xrpl::test diff --git a/src/tests/libxrpl/helpers/TxTest.cpp b/src/tests/libxrpl/helpers/TxTest.cpp index 32667ba13d..aeaa805b27 100644 --- a/src/tests/libxrpl/helpers/TxTest.cpp +++ b/src/tests/libxrpl/helpers/TxTest.cpp @@ -123,7 +123,7 @@ void TxTest::createAccount(Account const& account, XRPAmount xrp, uint32_t accountFlags) { auto const paymentTer = - submit(transactions::PaymentBuilder{Account::master, account, xrp}, Account::master).ter; + submit(transactions::PaymentBuilder{Account::kMaster, account, xrp}, Account::kMaster).ter; if (paymentTer != tesSUCCESS) { diff --git a/src/tests/libxrpl/helpers/TxTest.h b/src/tests/libxrpl/helpers/TxTest.h index 0b578c7e7f..1b3ce460a2 100644 --- a/src/tests/libxrpl/helpers/TxTest.h +++ b/src/tests/libxrpl/helpers/TxTest.h @@ -44,7 +44,7 @@ namespace xrpl::test { */ template constexpr XRPAmount -XRP(T xrp) +XRP(T xrp) // NOLINT(readability-identifier-naming) { return XRPAmount{static_cast(xrp) * kDropsPerXrp.drops()}; } diff --git a/src/tests/libxrpl/tx/AccountSet.cpp b/src/tests/libxrpl/tx/AccountSet.cpp index d00df152ae..726e6e9024 100644 --- a/src/tests/libxrpl/tx/AccountSet.cpp +++ b/src/tests/libxrpl/tx/AccountSet.cpp @@ -432,13 +432,13 @@ TEST(AccountSet, TransferRate) // Test data: {rate to set, expected TER, expected stored rate} std::vector const testData = { - {1.0, tesSUCCESS, 1.0}, - {1.1, tesSUCCESS, 1.1}, - {2.0, tesSUCCESS, 2.0}, - {2.1, temBAD_TRANSFER_RATE, 2.0}, // > 2.0 is invalid - {0.0, tesSUCCESS, 1.0}, // 0 clears the rate (default = 1.0) - {2.0, tesSUCCESS, 2.0}, - {0.9, temBAD_TRANSFER_RATE, 2.0}, // < 1.0 is invalid + {.set = 1.0, .code = tesSUCCESS, .get = 1.0}, + {.set = 1.1, .code = tesSUCCESS, .get = 1.1}, + {.set = 2.0, .code = tesSUCCESS, .get = 2.0}, + {.set = 2.1, .code = temBAD_TRANSFER_RATE, .get = 2.0}, // > 2.0 is invalid + {.set = 0.0, .code = tesSUCCESS, .get = 1.0}, // 0 clears; default rate is 1.0 + {.set = 2.0, .code = tesSUCCESS, .get = 2.0}, + {.set = 0.9, .code = temBAD_TRANSFER_RATE, .get = 2.0}, // < 1.0 is invalid }; TxTest env; From 9623e67b761f2ffd191d9abfe60c5acd82c7e1fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 15:35:52 -0400 Subject: [PATCH 09/28] ci: [DEPENDABOT] bump docker/login-action from 4.1.0 to 4.2.0 (#7318) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-nix-image.yml | 2 +- .github/workflows/reusable-build-docker-image.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-nix-image.yml b/.github/workflows/build-nix-image.yml index a8fb6eec86..e124c8afd7 100644 --- a/.github/workflows/build-nix-image.yml +++ b/.github/workflows/build-nix-image.yml @@ -92,7 +92,7 @@ jobs: type=raw,value=latest - name: Login to GitHub Container Registry - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.repository_owner }} diff --git a/.github/workflows/reusable-build-docker-image.yml b/.github/workflows/reusable-build-docker-image.yml index e631e02368..f919a550ed 100644 --- a/.github/workflows/reusable-build-docker-image.yml +++ b/.github/workflows/reusable-build-docker-image.yml @@ -60,7 +60,7 @@ jobs: - name: Login to GitHub Container Registry if: inputs.push - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.repository_owner }} From 7c597865657ccf6ca8b8b89cdbd5a5506fdcddb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 15:36:00 -0400 Subject: [PATCH 10/28] ci: [DEPENDABOT] bump docker/metadata-action from 6.0.0 to 6.1.0 (#7319) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-nix-image.yml | 2 +- .github/workflows/reusable-build-docker-image.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-nix-image.yml b/.github/workflows/build-nix-image.yml index e124c8afd7..3987b48099 100644 --- a/.github/workflows/build-nix-image.yml +++ b/.github/workflows/build-nix-image.yml @@ -84,7 +84,7 @@ jobs: - name: Docker metadata id: meta - uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 + uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 with: images: ${{ env.IMAGE_NAME }} tags: | diff --git a/.github/workflows/reusable-build-docker-image.yml b/.github/workflows/reusable-build-docker-image.yml index f919a550ed..9bb7b06186 100644 --- a/.github/workflows/reusable-build-docker-image.yml +++ b/.github/workflows/reusable-build-docker-image.yml @@ -68,7 +68,7 @@ jobs: - name: Docker metadata id: meta - uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 + uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 with: images: ${{ inputs.image_name }} tags: | From 4584b01bde3d80d00a8813d9a14f6101162ba8e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 15:36:13 -0400 Subject: [PATCH 11/28] ci: [DEPENDABOT] bump docker/build-push-action from 7.1.0 to 7.2.0 (#7320) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/reusable-build-docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-build-docker-image.yml b/.github/workflows/reusable-build-docker-image.yml index 9bb7b06186..5d60dc29fd 100644 --- a/.github/workflows/reusable-build-docker-image.yml +++ b/.github/workflows/reusable-build-docker-image.yml @@ -78,7 +78,7 @@ jobs: suffix=-${{ steps.vars.outputs.arch }},onlatest=true - name: Build and push - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 with: context: . file: ${{ inputs.dockerfile }} From 108a4c8217b5d84dc10dcfce97ec8404fc622883 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 15:36:21 -0400 Subject: [PATCH 12/28] ci: [DEPENDABOT] bump codecov/codecov-action from 6.0.0 to 6.0.1 (#7321) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/reusable-build-test-config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index cc927512ea..4f9b926e98 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -324,7 +324,7 @@ jobs: - name: Upload coverage report if: ${{ github.repository == 'XRPLF/rippled' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }} - uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 + uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 with: disable_search: true disable_telem: true From 2a0feca46b4034f215cb5b4dbe068c836031fa97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 19:36:32 +0000 Subject: [PATCH 13/28] ci: [DEPENDABOT] bump docker/setup-buildx-action from 4.0.0 to 4.1.0 (#7322) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-nix-image.yml | 2 +- .github/workflows/reusable-build-docker-image.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-nix-image.yml b/.github/workflows/build-nix-image.yml index 3987b48099..fde808642c 100644 --- a/.github/workflows/build-nix-image.yml +++ b/.github/workflows/build-nix-image.yml @@ -80,7 +80,7 @@ jobs: steps: - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Docker metadata id: meta diff --git a/.github/workflows/reusable-build-docker-image.yml b/.github/workflows/reusable-build-docker-image.yml index 5d60dc29fd..5b555b713f 100644 --- a/.github/workflows/reusable-build-docker-image.yml +++ b/.github/workflows/reusable-build-docker-image.yml @@ -56,7 +56,7 @@ jobs: echo "arch=${PLATFORM##*/}" >> $GITHUB_OUTPUT - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Login to GitHub Container Registry if: inputs.push From 1162371def825beed3888f79b9f253dea165c13d Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 26 May 2026 21:03:04 +0100 Subject: [PATCH 14/28] ci: Only push docker images in XRPLF/rippled (#7330) --- .github/workflows/build-nix-image.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-nix-image.yml b/.github/workflows/build-nix-image.yml index fde808642c..edd35132fa 100644 --- a/.github/workflows/build-nix-image.yml +++ b/.github/workflows/build-nix-image.yml @@ -61,12 +61,12 @@ jobs: base_image: ${{ matrix.distro.base_image }} platform: ${{ matrix.target.platform }} runner: ${{ matrix.target.runner }} - push: ${{ github.event_name == 'push' }} + push: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} merge: name: Merge ${{ matrix.distro }} manifest needs: build - if: github.event_name == 'push' + if: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} runs-on: ubuntu-latest permissions: contents: read From 7da643d8648959f83d5d6a7d5d3a869171dbf1ca Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Wed, 27 May 2026 11:19:20 -0400 Subject: [PATCH 15/28] fix: Fix a rounding error at the `Number::maxRep` cusp (#7051) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com> --- include/xrpl/basics/Number.h | 137 +++++---- include/xrpl/protocol/Rules.h | 13 + include/xrpl/protocol/STAmount.h | 2 +- src/libxrpl/basics/Number.cpp | 351 ++++++++++++++++-------- src/libxrpl/protocol/IOUAmount.cpp | 2 +- src/libxrpl/protocol/Rules.cpp | 62 ++++- src/libxrpl/protocol/STNumber.cpp | 3 +- src/libxrpl/tx/applySteps.cpp | 18 +- src/test/app/AMMExtended_test.cpp | 109 +++----- src/test/app/AMM_test.cpp | 9 - src/test/app/Invariants_test.cpp | 206 +++++++------- src/test/basics/IOUAmount_test.cpp | 3 +- src/test/basics/Number_test.cpp | 86 +++++- src/test/protocol/STNumber_test.cpp | 3 +- src/test/rpc/GetAggregatePrice_test.cpp | 3 +- 15 files changed, 646 insertions(+), 361 deletions(-) diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index e67f1f534d..3aeb4b5bcd 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include namespace xrpl { @@ -44,11 +46,11 @@ isPowerOfTen(T value) * * min is a power of 10, and * * max = min * 10 - 1. * - * The mantissa_scale enum indicates whether the range is "small" or "large". - * This intentionally restricts the number of MantissaRanges that can be - * instantiated to two: one for each scale. + * The MantissaScale enum indicates properties of the range: size, and some behavioral + * options. This intentionally restricts the number of unique MantissaRanges that can + * be instantiated: one for each scale. * - * The "small" scale is based on the behavior of STAmount for IOUs. It has a min + * The "Small" scale is based on the behavior of STAmount for IOUs. It has a min * value of 10^15, and a max value of 10^16-1. This was sufficient for * uses before Lending Protocol was implemented, mostly related to AMM. * @@ -59,29 +61,54 @@ isPowerOfTen(T value) * STNumber field type, and for internal calculations. That necessitated the * "large" scale. * - * The "large" scale is intended to represent all values that can be represented + * The "Large" scales are intended to represent all values that can be represented * by an STAmount - IOUs, XRP, and MPTs. It has a min value of 10^18, and a max - * value of 10^19-1. + * value of 10^19-1. "LargeLegacy" is like "Large", but preserves + * a rounding error when a computation results in a mantissa of + * Number::kMaxRep that needs to be rounded up, but rounds down + * instead. It will maintain consistent behavior until the fixCleanup3_2_0 + * amendment is enabled. * * Note that if the mentioned amendments are eventually retired, this class - * should be left in place, but the "small" scale option should be removed. This + * should be left in place, but the "Small" scale option should be removed. This * will allow for future expansion beyond 64-bits if it is ever needed. */ -struct MantissaRange +struct MantissaRange final { using rep = std::uint64_t; - enum class MantissaScale { Small, Large }; + enum class MantissaScale { + Small, + // LargeLegacy can be removed when fixCleanup3_2_0 is retired + LargeLegacy, + Large, + }; + + // This entire enum can be removed when fixCleanup3_2_0 is retired + enum class CuspRoundingFix : bool { + Disabled = false, + Enabled = true, + }; explicit constexpr MantissaRange(MantissaScale scale) - : min(getMin(scale)), log(logTen(min).value_or(-1)), scale(scale) + : min(getMin(scale)) + , cuspRoundingFixEnabled(isCuspFixEnabled(scale)) + , log(logTen(min).value_or(-1)) + , scale(scale) { } rep min; rep max{(min * 10) - 1}; + CuspRoundingFix cuspRoundingFixEnabled; int log; MantissaScale scale; + static MantissaRange const& + getMantissaRange(MantissaScale scale); + + static std::set const& + getAllScales(); + private: static constexpr rep getMin(MantissaScale scale) @@ -90,15 +117,35 @@ private: { case MantissaScale::Small: return 1'000'000'000'000'000ULL; + case MantissaScale::LargeLegacy: case MantissaScale::Large: return 1'000'000'000'000'000'000ULL; default: - // Since this can never be called outside a non-constexpr - // context, this throw assures that the build fails if an + // If called in a constexpr context, this throw assures that the build fails if an // invalid scale is used. - throw std::runtime_error("Unknown mantissa scale"); + throw std::runtime_error("Unknown mantissa scale"); // LCOV_EXCL_LINE } } + + static constexpr CuspRoundingFix + isCuspFixEnabled(MantissaScale scale) + { + switch (scale) + { + case MantissaScale::Small: + case MantissaScale::LargeLegacy: + return CuspRoundingFix::Disabled; + case MantissaScale::Large: + return CuspRoundingFix::Enabled; + default: + // If called in a constexpr context, this throw assures that the build fails if an + // invalid scale is used. + throw std::runtime_error("Unknown mantissa scale"); // LCOV_EXCL_LINE + } + } + + static std::unordered_map const& + getRanges(); }; // Like std::integral, but only 64-bit integral types. @@ -203,7 +250,7 @@ concept Integral64 = std::is_same_v || std::is_same_v + template < + auto MinMantissa, + auto MaxMantissa, + Integral64 T = std::decay_t, + Integral64 TMax = std::decay_t> [[nodiscard]] std::pair - normalizeToRange(T minMantissa, T maxMantissa) const; + normalizeToRange() const; private: static thread_local RoundingMode mode; // The available ranges for mantissa - static constexpr MantissaRange kSmallRange{MantissaRange::MantissaScale::Small}; - static_assert(isPowerOfTen(kSmallRange.min)); - static_assert(kSmallRange.min == 1'000'000'000'000'000LL); - static_assert(kSmallRange.max == 9'999'999'999'999'999LL); - static_assert(kSmallRange.log == 15); - static_assert(kSmallRange.min < kMaxRep); - static_assert(kSmallRange.max < kMaxRep); - static constexpr MantissaRange kLargeRange{MantissaRange::MantissaScale::Large}; - static_assert(isPowerOfTen(kLargeRange.min)); - static_assert(kLargeRange.min == 1'000'000'000'000'000'000ULL); - static_assert(kLargeRange.max == internalrep(9'999'999'999'999'999'999ULL)); - static_assert(kLargeRange.log == 18); - static_assert(kLargeRange.min < kMaxRep); - static_assert(kLargeRange.max > kMaxRep); - // The range for the mantissa when normalized. // Use reference_wrapper to avoid making copies, and prevent accidentally // changing the values inside the range. static thread_local std::reference_wrapper kRange; void - normalize(); + normalize(MantissaRange const& range); /** Normalize Number components to an arbitrary range. * @@ -481,7 +508,8 @@ private: T& mantissa, int& exponent, internalrep const& minMantissa, - internalrep const& maxMantissa); + internalrep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled); template friend void @@ -490,7 +518,8 @@ private: T& mantissa, int& exponent, MantissaRange::rep const& minMantissa, - MantissaRange::rep const& maxMantissa); + MantissaRange::rep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled); [[nodiscard]] bool isnormal() const noexcept; @@ -526,7 +555,7 @@ static constexpr Number kNumZero{}; inline Number::Number(bool negative, internalrep mantissa, int exponent, Normalized) : Number(negative, mantissa, exponent, Unchecked{}) { - normalize(); + normalize(kRange); } inline Number::Number(internalrep mantissa, int exponent, Normalized) @@ -696,10 +725,19 @@ Number::isnormal() const noexcept kMinExponent <= exponent_ && exponent_ <= kMaxExponent); } -template +template std::pair -Number::normalizeToRange(T minMantissa, T maxMantissa) const +Number::normalizeToRange() const { + static_assert(std::is_same_v || std::is_same_v); + static_assert(std::is_same_v); + auto constexpr kMIN = static_cast(MinMantissa); + auto constexpr kMAX = static_cast(MaxMantissa); + static_assert(kMIN > 0); + static_assert(kMIN % 10 == 0); + static_assert(kMAX % 10 == 9); + static_assert((kMAX + 1) / 10 == kMIN); + bool negative = negative_; internalrep mantissa = mantissa_; int exponent = exponent_; @@ -711,7 +749,10 @@ Number::normalizeToRange(T minMantissa, T maxMantissa) const "xrpl::Number::normalizeToRange", "Number is non-negative for unsigned range."); } - Number::normalize(negative, mantissa, exponent, minMantissa, maxMantissa); + // Don't need to worry about the cuspRounding fix because rounding up will never take the + // mantissa over maxMantissa with a ones digit value other than 0. 0 can safely be truncated. + Number::normalize( + negative, mantissa, exponent, kMIN, kMAX, MantissaRange::CuspRoundingFix::Disabled); auto const sign = negative ? -1 : 1; return std::make_pair(static_cast(sign * mantissa), exponent); @@ -763,6 +804,8 @@ to_string(MantissaRange::MantissaScale const& scale) { case MantissaRange::MantissaScale::Small: return "small"; + case MantissaRange::MantissaScale::LargeLegacy: + return "largeLegacy"; case MantissaRange::MantissaScale::Large: return "large"; default: diff --git a/include/xrpl/protocol/Rules.h b/include/xrpl/protocol/Rules.h index fbbd3d8805..9c17ff2391 100644 --- a/include/xrpl/protocol/Rules.h +++ b/include/xrpl/protocol/Rules.h @@ -122,4 +122,17 @@ private: std::optional saved_; }; +class NumberSO; +class NumberMantissaScaleGuard; + +bool +useRulesGuards(Rules const& rules); + +void +createGuards( + Rules const& rules, + std::optional& stNumberSO, + std::optional& rulesGuard, + std::optional& mantissaScaleGuard); + } // namespace xrpl diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index a4fffad40c..1a5b442d8b 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -559,7 +559,7 @@ STAmount::fromNumber(A const& a, Number const& number) return STAmount{asset, intValue, 0, negative}; } - auto const [mantissa, exponent] = working.normalizeToRange(kMinValue, kMaxValue); + auto const [mantissa, exponent] = working.normalizeToRange(); return STAmount{asset, mantissa, exponent, negative}; } diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index 06bd78d8b0..11f5934b04 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -10,9 +10,11 @@ #include #include #include +#include #include #include #include +#include #include #ifdef _MSC_VER @@ -28,7 +30,76 @@ using int128_t = __int128_t; namespace xrpl { thread_local Number::RoundingMode Number::mode = Number::RoundingMode::ToNearest; -thread_local std::reference_wrapper Number::kRange = kLargeRange; +thread_local std::reference_wrapper Number::kRange = + MantissaRange::getMantissaRange(MantissaRange::MantissaScale::Large); + +std::set const& +MantissaRange::getAllScales() +{ + static std::set const kScales = { + MantissaRange::MantissaScale::Small, + MantissaRange::MantissaScale::LargeLegacy, + MantissaRange::MantissaScale::Large, + }; + return kScales; +} + +std::unordered_map const& +MantissaRange::getRanges() +{ + static auto const kMap = []() { + std::unordered_map map; + for (auto const scale : getAllScales()) + { + map.emplace(scale, scale); + } + + // Use these constexpr declarations to do static_asserts to verify the MantissaRanges are + // created correctly, but nothing else. + { + [[maybe_unused]] + constexpr static MantissaRange kRange{MantissaRange::MantissaScale::Small}; + static_assert(isPowerOfTen(kRange.min)); + static_assert(kRange.min == 1'000'000'000'000'000LL); + static_assert(kRange.max == 9'999'999'999'999'999LL); + static_assert(kRange.log == 15); + static_assert(kRange.min < Number::kMaxRep); + static_assert(kRange.max < Number::kMaxRep); + static_assert(kRange.cuspRoundingFixEnabled == CuspRoundingFix::Disabled); + } + { + [[maybe_unused]] + constexpr static MantissaRange kRange{MantissaRange::MantissaScale::LargeLegacy}; + static_assert(isPowerOfTen(kRange.min)); + static_assert(kRange.min == 1'000'000'000'000'000'000ULL); + static_assert(kRange.max == rep(9'999'999'999'999'999'999ULL)); + static_assert(kRange.log == 18); + static_assert(kRange.min < Number::kMaxRep); + static_assert(kRange.max > Number::kMaxRep); + static_assert(kRange.cuspRoundingFixEnabled == CuspRoundingFix::Disabled); + } + { + [[maybe_unused]] + constexpr static MantissaRange kRange{MantissaRange::MantissaScale::Large}; + static_assert(isPowerOfTen(kRange.min)); + static_assert(kRange.min == 1'000'000'000'000'000'000ULL); + static_assert(kRange.max == rep(9'999'999'999'999'999'999ULL)); + static_assert(kRange.log == 18); + static_assert(kRange.min < Number::kMaxRep); + static_assert(kRange.max > Number::kMaxRep); + static_assert(kRange.cuspRoundingFixEnabled == CuspRoundingFix::Enabled); + } + return map; + }(); + + return kMap; +} + +MantissaRange const& +MantissaRange::getMantissaRange(MantissaScale scale) +{ + return getRanges().at(scale); +} Number::RoundingMode Number::getround() @@ -51,10 +122,37 @@ Number::getMantissaScale() void Number::setMantissaScale(MantissaRange::MantissaScale scale) { - if (scale != MantissaRange::MantissaScale::Small && - scale != MantissaRange::MantissaScale::Large) + if (!MantissaRange::getAllScales().contains(scale)) logicError("Unknown mantissa scale"); - kRange = scale == MantissaRange::MantissaScale::Small ? kSmallRange : kLargeRange; + kRange = MantissaRange::getMantissaRange(scale); +} + +// Optimization equivalent to: +// auto r = static_cast(u % 10); +// u /= 10; +// return r; +// Derived from Hacker's Delight Second Edition Chapter 10 +// by Henry S. Warren, Jr. +static inline unsigned +divu10(uint128_t& u) +{ + // q = u * 0.75 + auto q = (u >> 1) + (u >> 2); + // iterate towards q = u * 0.8 + q += q >> 4; + q += q >> 8; + q += q >> 16; + q += q >> 32; + q += q >> 64; + // q /= 8 approximately == u / 10 + q >>= 3; + // r = u - q * 10 approximately == u % 10 + auto r = static_cast(u - ((q << 3) + (q << 1))); + // correction c is 1 if r >= 10 else 0 + auto c = (r + 6) >> 4; + u = q + c; + r -= c * 10; + return r; } // Guard @@ -92,6 +190,18 @@ public: unsigned pop() noexcept; + /** Drop a digit from the mantissa, and increment the exponent, storing the dropped digit in + * this Guard. + * + * Substitute for: + push(mantissa % 10); + mantissa /= 10; + ++exponent; + */ + template + void + doDropDigit(T& mantissa, int& exponent) noexcept; + // Indicate round direction: 1 is up, -1 is down, 0 is even // This enables the client to round towards nearest, and on // tie, round towards even. @@ -107,6 +217,7 @@ public: int& exponent, internalrep const& minMantissa, internalrep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled, std::string location); // Modify the result to the correctly rounded value @@ -168,6 +279,27 @@ Number::Guard::pop() noexcept return d; } +template +void +Number::Guard::doDropDigit(T& mantissa, int& exponent) noexcept +{ + push(mantissa % 10); + mantissa /= 10; + ++exponent; +} + +// Use the divu10 optimization for uint128s +template <> +void +Number::Guard::doDropDigit(uint128_t& mantissa, int& exponent) noexcept +{ + // The following is optimization for: + // push(static_cast(mantissa % 10)); + // mantissa /= 10; + push(divu10(mantissa)); + ++exponent; +} + // Returns: // -1 if Guard is less than half // 0 if Guard is exactly half @@ -242,18 +374,60 @@ Number::Guard::doRoundUp( int& exponent, internalrep const& minMantissa, internalrep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled, std::string location) { auto r = round(); if (r == 1 || (r == 0 && (mantissa & 1) == 1)) { - ++mantissa; - // Ensure mantissa after incrementing fits within both the - // min/maxMantissa range and is a valid "rep". - if (mantissa > maxMantissa || mantissa > kMaxRep) + auto const safeToIncrement = [&maxMantissa](auto const& mantissa) { + return mantissa < maxMantissa && mantissa < kMaxRep; + }; + if (cuspRoundingFixEnabled == MantissaRange::CuspRoundingFix::Enabled) { - mantissa /= 10; - ++exponent; + // Ensure mantissa after incrementing fits within both the + // min/maxMantissa range and is a valid "rep". + if (safeToIncrement(mantissa)) + { + // Nothing unusual here, just increment the mantissa + ++mantissa; + } + else + { + // Incrementing the mantissa will require dividing, which will require rounding. So + // _don't_ increment the mantissa. Instead, divide and round recursively. It should + // be impossible to recurse more than once, because once the mantissa is divided by + // 10, it will be _well_ under maxMantissa and kMaxRep, so adding 1 will have no + // change of bringing it back over. + doDropDigit(mantissa, exponent); + XRPL_ASSERT_PARTS( + safeToIncrement(mantissa), + "xrpl::Number::Guard::doRoundUp", + "can't recurse more than once"); + doRoundUp( + negative, + mantissa, + exponent, + minMantissa, + maxMantissa, + cuspRoundingFixEnabled, + location); + return; + } + } + else + { + // Need to preserve the incorrect behavior until the fix amendment can be retired, + // because otherwise would risk an unplanned ledger fork. + ++mantissa; + // Ensure mantissa after incrementing fits within both the + // min/maxMantissa range and is a valid "rep". + if (mantissa > maxMantissa || mantissa > kMaxRep) + { + // Don't use doDropDigit here + mantissa /= 10; + ++exponent; + } } } bringIntoRange(negative, mantissa, exponent, minMantissa); @@ -293,9 +467,9 @@ Number::Guard::doRound(rep& drops, std::string location) const { static_assert(sizeof(internalrep) == sizeof(rep)); // This should be impossible, because it's impossible to represent - // "maxRep + 0.6" in Number, regardless of the scale. There aren't - // enough digits available. You'd either get a mantissa of "maxRep" - // or "(maxRep + 1) / 10", neither of which will round up when + // "kMaxRep + 0.6" in Number, regardless of the scale. There aren't + // enough digits available. You'd either get a mantissa of "kMaxRep" + // or "(kMaxRep + 1) / 10", neither of which will round up when // converting to rep, though the latter might overflow _before_ // rounding. Throw(std::string(location)); // LCOV_EXCL_LINE @@ -331,29 +505,11 @@ Number::externalToInternal(rep mantissa) return static_cast(-temp); } -constexpr Number -Number::oneSmall() -{ - return Number{false, Number::kSmallRange.min, -Number::kSmallRange.log, Number::Unchecked{}}; -}; - -constexpr Number kOneSml = Number::oneSmall(); - -constexpr Number -Number::oneLarge() -{ - return Number{false, Number::kLargeRange.min, -Number::kLargeRange.log, Number::Unchecked{}}; -}; - -constexpr Number kOneLrg = Number::oneLarge(); - Number Number::one() { - if (&kRange.get() == &kSmallRange) - return kOneSml; - XRPL_ASSERT(&kRange.get() == &kLargeRange, "Number::one() : valid range"); - return kOneLrg; + auto const& range = kRange.get(); + return Number{false, range.min, -range.log, Number::Unchecked{}}; } // Use the member names in this static function for now so the diff is cleaner @@ -365,7 +521,8 @@ doNormalize( T& mantissa, int& exponent, MantissaRange::rep const& minMantissa, - MantissaRange::rep const& maxMantissa) + MantissaRange::rep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) { static constexpr auto kMinExponent = Number::kMinExponent; static constexpr auto kMaxExponent = Number::kMaxExponent; @@ -394,9 +551,7 @@ doNormalize( { if (exponent >= kMaxExponent) throw std::overflow_error("Number::normalize 1"); - g.push(m % 10); - m /= 10; - ++exponent; + g.doDropDigit(m, exponent); } if ((exponent < kMinExponent) || (m < minMantissa)) { @@ -407,7 +562,7 @@ doNormalize( } // When using the largeRange, "m" needs fit within an int64, even if - // the final mantissa_ is going to end up larger to fit within the + // the final mantissa is going to end up larger to fit within the // MantissaRange. Cut it down here so that the rounding will be done while // it's smaller. // @@ -415,26 +570,31 @@ doNormalize( // so "m" will be modified to 990,000,000,000,012,345. Then that value // will be rounded to 990,000,000,000,012,345 or // 990,000,000,000,012,346, depending on the rounding mode. Finally, - // mantissa_ will be "m*10" so it fits within the range, and end up as + // mantissa will be "m*10" so it fits within the range, and end up as // 9,900,000,000,000,123,450 or 9,900,000,000,000,123,460. - // mantissa() will return mantissa_ / 10, and exponent() will return - // exponent_ + 1. + // mantissa() will return mantissa / 10, and exponent() will return + // exponent + 1. if (m > kMaxRep) { if (exponent >= kMaxExponent) throw std::overflow_error("Number::normalize 1.5"); - g.push(m % 10); - m /= 10; - ++exponent; + g.doDropDigit(m, exponent); } // Before modification, m should be within the min/max range. After - // modification, it must be less than maxRep. In other words, the original - // value should have been no more than maxRep * 10. - // (maxRep * 10 > maxMantissa) + // modification, it must be less than kMaxRep. In other words, the original + // value should have been no more than kMaxRep * 10. + // (kMaxRep * 10 > maxMantissa) XRPL_ASSERT_PARTS(m <= kMaxRep, "xrpl::doNormalize", "intermediate mantissa fits in int64"); mantissa = m; - g.doRoundUp(negative, mantissa, exponent, minMantissa, maxMantissa, "Number::normalize 2"); + g.doRoundUp( + negative, + mantissa, + exponent, + minMantissa, + maxMantissa, + cuspRoundingFixEnabled, + "Number::normalize 2"); XRPL_ASSERT_PARTS( mantissa >= minMantissa && mantissa <= maxMantissa, "xrpl::doNormalize", @@ -448,9 +608,10 @@ Number::normalize( uint128_t& mantissa, int& exponent, internalrep const& minMantissa, - internalrep const& maxMantissa) + internalrep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) { - doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa); + doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled); } template <> @@ -460,9 +621,10 @@ Number::normalize( unsigned long long& mantissa, int& exponent, internalrep const& minMantissa, - internalrep const& maxMantissa) + internalrep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) { - doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa); + doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled); } template <> @@ -472,16 +634,16 @@ Number::normalize( unsigned long& mantissa, int& exponent, internalrep const& minMantissa, - internalrep const& maxMantissa) + internalrep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) { - doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa); + doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled); } void -Number::normalize() +Number::normalize(MantissaRange const& range) { - auto const& range = kRange.get(); - normalize(negative_, mantissa_, exponent_, range.min, range.max); + normalize(negative_, mantissa_, exponent_, range.min, range.max, range.cuspRoundingFixEnabled); } // Copy the number, but set a new exponent. Because the mantissa doesn't change, @@ -542,9 +704,7 @@ Number::operator+=(Number const& y) g.setNegative(); do { - g.push(xm % 10); - xm /= 10; - ++xe; + g.doDropDigit(xm, xe); } while (xe < ye); } else if (xe > ye) @@ -553,26 +713,30 @@ Number::operator+=(Number const& y) g.setNegative(); do { - g.push(ym % 10); - ym /= 10; - ++ye; + g.doDropDigit(ym, ye); } while (xe > ye); } auto const& range = kRange.get(); auto const& minMantissa = range.min; auto const& maxMantissa = range.max; + auto const cuspRoundingFixEnabled = range.cuspRoundingFixEnabled; if (xn == yn) { xm += ym; if (xm > maxMantissa || xm > kMaxRep) { - g.push(xm % 10); - xm /= 10; - ++xe; + g.doDropDigit(xm, xe); } - g.doRoundUp(xn, xm, xe, minMantissa, maxMantissa, "Number::addition overflow"); + g.doRoundUp( + xn, + xm, + xe, + minMantissa, + maxMantissa, + cuspRoundingFixEnabled, + "Number::addition overflow"); } else { @@ -598,38 +762,10 @@ Number::operator+=(Number const& y) negative_ = xn; mantissa_ = static_cast(xm); exponent_ = xe; - normalize(); + normalize(range); return *this; } -// Optimization equivalent to: -// auto r = static_cast(u % 10); -// u /= 10; -// return r; -// Derived from Hacker's Delight Second Edition Chapter 10 -// by Henry S. Warren, Jr. -static inline unsigned -divu10(uint128_t& u) -{ - // q = u * 0.75 - auto q = (u >> 1) + (u >> 2); - // iterate towards q = u * 0.8 - q += q >> 4; - q += q >> 8; - q += q >> 16; - q += q >> 32; - q += q >> 64; - // q /= 8 approximately == u / 10 - q >>= 3; - // r = u - q * 10 approximately == u % 10 - auto r = static_cast(u - ((q << 3) + (q << 1))); - // correction c is 1 if r >= 10 else 0 - auto c = (r + 6) >> 4; - u = q + c; - r -= c * 10; - return r; -} - Number& Number::operator*=(Number const& y) { @@ -667,15 +803,13 @@ Number::operator*=(Number const& y) auto const& range = kRange.get(); auto const& minMantissa = range.min; auto const& maxMantissa = range.max; + auto const cuspRoundingFixEnabled = range.cuspRoundingFixEnabled; while (zm > maxMantissa || zm > kMaxRep) { - // The following is optimization for: - // g.push(static_cast(zm % 10)); - // zm /= 10; - g.push(divu10(zm)); - ++ze; + g.doDropDigit(zm, ze); } + xm = static_cast(zm); xe = ze; g.doRoundUp( @@ -684,12 +818,13 @@ Number::operator*=(Number const& y) xe, minMantissa, maxMantissa, + cuspRoundingFixEnabled, "Number::multiplication overflow : exponent is " + std::to_string(xe)); negative_ = zn; mantissa_ = xm; exponent_ = xe; - normalize(); + normalize(range); return *this; } @@ -721,6 +856,7 @@ Number::operator/=(Number const& y) auto const& range = kRange.get(); auto const& minMantissa = range.min; auto const& maxMantissa = range.max; + auto const cuspRoundingFixEnabled = range.cuspRoundingFixEnabled; // Shift by 10^17 gives greatest precision while not overflowing // uint128_t or the cast back to int64_t @@ -728,8 +864,6 @@ Number::operator/=(Number const& y) // log(2^128,10) ~ 38.5 // largeRange.log = 18, fits in 10^19 // f can be up to 10^(38-19) = 10^19 safely - static_assert(kSmallRange.log == 15); - static_assert(kLargeRange.log == 18); bool const small = Number::getMantissaScale() == MantissaRange::MantissaScale::Small; uint128_t const f = small ? 100'000'000'000'000'000 : 10'000'000'000'000'000'000ULL; XRPL_ASSERT_PARTS(f >= minMantissa * 10, "Number::operator/=", "factor expected size"); @@ -779,7 +913,7 @@ Number::operator/=(Number const& y) ze -= 3; } } - normalize(zn, zm, ze, minMantissa, maxMantissa); + normalize(zn, zm, ze, minMantissa, maxMantissa, cuspRoundingFixEnabled); negative_ = zn; mantissa_ = static_cast(zm); exponent_ = ze; @@ -801,10 +935,9 @@ operator rep() const g.setNegative(); drops = -drops; } - for (; offset < 0; ++offset) + while (offset < 0) { - g.push(drops % 10); - drops /= 10; + g.doDropDigit(drops, offset); } for (; offset > 0; --offset) { @@ -831,7 +964,7 @@ Number::truncate() const noexcept } // We are guaranteed that normalize() will never throw an exception // because exponent is either negative or zero at this point. - ret.normalize(); + ret.normalize(kRange); return ret; } diff --git a/src/libxrpl/protocol/IOUAmount.cpp b/src/libxrpl/protocol/IOUAmount.cpp index d65ba41a01..d214995809 100644 --- a/src/libxrpl/protocol/IOUAmount.cpp +++ b/src/libxrpl/protocol/IOUAmount.cpp @@ -56,7 +56,7 @@ IOUAmount::fromNumber(Number const& number) // to normalize, which calls fromNumber IOUAmount result{}; std::tie(result.mantissa_, result.exponent_) = - number.normalizeToRange(kMinMantissa, kMaxMantissa); + number.normalizeToRange(); return result; } diff --git a/src/libxrpl/protocol/Rules.cpp b/src/libxrpl/protocol/Rules.cpp index 2c971749b6..08a95145eb 100644 --- a/src/libxrpl/protocol/Rules.cpp +++ b/src/libxrpl/protocol/Rules.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -38,15 +39,68 @@ setCurrentTransactionRules(std::optional r) // Make global changes associated with the rules before the value is moved. // Push the appropriate setting, instead of having the class pull every time // the value is needed. That could get expensive fast. - bool const enableLargeNumbers = + + // If any new conditions with new amendments are added, those amendments must also be added to + // useRulesGuards. + bool const enableVaultNumbers = !r || (r->enabled(featureSingleAssetVault) || r->enabled(featureLendingProtocol)); - Number::setMantissaScale( - enableLargeNumbers ? MantissaRange::MantissaScale::Large - : MantissaRange::MantissaScale::Small); + bool const enableCuspRoundingFix = !r || r->enabled(fixCleanup3_2_0); + XRPL_ASSERT( + !r || useRulesGuards(*r) == (enableCuspRoundingFix || enableVaultNumbers), + "setCurrentTransactionRules : rule decisions match"); + + // Declare the range this way to keep clang-tidy from complaining + auto const range = [enableCuspRoundingFix, enableVaultNumbers]() { + if (enableVaultNumbers) + { + if (enableCuspRoundingFix) + { + return MantissaRange::MantissaScale::Large; + } + return MantissaRange::MantissaScale::LargeLegacy; + } + return MantissaRange::MantissaScale::Small; + }(); + Number::setMantissaScale(range); *getCurrentTransactionRulesRef() = std::move(r); } +bool +useRulesGuards(Rules const& rules) +{ + // The list of amendments used here - to decide whether to create a RulesGuard - must be a + // superset of the list used to figure out which mantissa scale to use in + // setCurrentTransactionRules. Additional amendments can be added if desired. + // + // As soon as any one of these amendments is retired, this whole function can be removed, along + // with createGuards, and any other callers, and the first set of guards can be created directly + // at the call site, without using optional. + return rules.enabled(fixCleanup3_2_0) || rules.enabled(featureSingleAssetVault) || + rules.enabled(featureLendingProtocol); +} + +void +createGuards( + Rules const& rules, + std::optional& stNumberSO, + std::optional& rulesGuard, + std::optional& mantissaScaleGuard) +{ + if (useRulesGuards(rules)) + { + // raii classes for the current ledger rules. + // fixUniversalNumber predates the rulesGuard and should be replaced. + stNumberSO.emplace(rules.enabled(fixUniversalNumber)); + rulesGuard.emplace(rules); + } + else + { + // Without those features enabled, always use the old number rules. + mantissaScaleGuard.emplace(MantissaRange::MantissaScale::Small); + } +} + class Rules::Impl { private: diff --git a/src/libxrpl/protocol/STNumber.cpp b/src/libxrpl/protocol/STNumber.cpp index aa3e83515c..8ef7b9760f 100644 --- a/src/libxrpl/protocol/STNumber.cpp +++ b/src/libxrpl/protocol/STNumber.cpp @@ -96,7 +96,8 @@ STNumber::add(Serializer& s) const // Json. Regardless, the only time we should be serializing an // STNumber is when the scale is large. XRPL_ASSERT_PARTS( - Number::getMantissaScale() == MantissaRange::MantissaScale::Large, + Number::getMantissaScale() == MantissaRange::MantissaScale::LargeLegacy || + Number::getMantissaScale() == MantissaRange::MantissaScale::Large, "xrpl::STNumber::add", "STNumber only used with large mantissa scale"); #endif diff --git a/src/libxrpl/tx/applySteps.cpp b/src/libxrpl/tx/applySteps.cpp index e0b1af80e2..217fdd717f 100644 --- a/src/libxrpl/tx/applySteps.cpp +++ b/src/libxrpl/tx/applySteps.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -66,26 +65,15 @@ withTxnType(Rules const& rules, TxType txnType, F&& f) // so these need to be more global. // // To prevent unintentional side effects on existing checks, they will be - // set for every operation only once SingleAssetVault (or later - // LendingProtocol) are enabled. + // set for every operation only once at least one of the relevant amendments + // are enabled. // // See also Transactor::operator(). // std::optional stNumberSO; std::optional rulesGuard; std::optional mantissaScaleGuard; - if (rules.enabled(featureSingleAssetVault) || rules.enabled(featureLendingProtocol)) - { - // raii classes for the current ledger rules. - // fixUniversalNumber predates the rulesGuard and should be replaced. - stNumberSO.emplace(rules.enabled(fixUniversalNumber)); - rulesGuard.emplace(rules); - } - else - { - // Without those features enabled, always use the old number rules. - mantissaScaleGuard.emplace(MantissaRange::MantissaScale::Small); - } + createGuards(rules, stNumberSO, rulesGuard, mantissaScaleGuard); switch (txnType) { diff --git a/src/test/app/AMMExtended_test.cpp b/src/test/app/AMMExtended_test.cpp index 18f2d6df2f..a0a7d0fb15 100644 --- a/src/test/app/AMMExtended_test.cpp +++ b/src/test/app/AMMExtended_test.cpp @@ -65,10 +65,15 @@ namespace xrpl::test { /** * Tests of AMM that use offers too. */ -struct AMMExtended_test : public jtx::AMMTest +class AMMExtended_test : public jtx::AMMTest { // Use small Number mantissas for the life of this test. - NumberMantissaScaleGuard const sg{xrpl::MantissaRange::MantissaScale::Small}; + NumberMantissaScaleGuard const sg_{xrpl::MantissaRange::MantissaScale::Small}; + + // For now, just disable SAV entirely, which locks in the small Number + // mantissas + FeatureBitset const all_{ + testableAmendments() - featureSingleAssetVault - featureLendingProtocol}; private: void @@ -1349,37 +1354,33 @@ private: testOffers() { using namespace jtx; - // For now, just disable SAV entirely, which locks in the small Number - // mantissas - FeatureBitset const all{ - testableAmendments() - featureSingleAssetVault - featureLendingProtocol}; - testRmFundedOffer(all); - testRmFundedOffer(all - fixAMMv1_1 - fixAMMv1_3); - testEnforceNoRipple(all); - testFillModes(all); - testOfferCrossWithXRP(all); - testOfferCrossWithLimitOverride(all); - testCurrencyConversionEntire(all); - testCurrencyConversionInParts(all); - testCrossCurrencyStartXRP(all); - testCrossCurrencyEndXRP(all); - testCrossCurrencyBridged(all); - testOfferFeesConsumeFunds(all); - testOfferCreateThenCross(all); - testSellFlagExceedLimit(all); - testGatewayCrossCurrency(all); - testGatewayCrossCurrency(all - fixAMMv1_1 - fixAMMv1_3); - testBridgedCross(all); - testSellWithFillOrKill(all); - testTransferRateOffer(all); - testSelfIssueOffer(all); - testBadPathAssert(all); - testSellFlagBasic(all); - testDirectToDirectPath(all); - testDirectToDirectPath(all - fixAMMv1_1 - fixAMMv1_3); - testRequireAuth(all); - testMissingAuth(all); + testRmFundedOffer(all_); + testRmFundedOffer(all_ - fixAMMv1_1 - fixAMMv1_3); + testEnforceNoRipple(all_); + testFillModes(all_); + testOfferCrossWithXRP(all_); + testOfferCrossWithLimitOverride(all_); + testCurrencyConversionEntire(all_); + testCurrencyConversionInParts(all_); + testCrossCurrencyStartXRP(all_); + testCrossCurrencyEndXRP(all_); + testCrossCurrencyBridged(all_); + testOfferFeesConsumeFunds(all_); + testOfferCreateThenCross(all_); + testSellFlagExceedLimit(all_); + testGatewayCrossCurrency(all_); + testGatewayCrossCurrency(all_ - fixAMMv1_1 - fixAMMv1_3); + testBridgedCross(all_); + testSellWithFillOrKill(all_); + testTransferRateOffer(all_); + testSelfIssueOffer(all_); + testBadPathAssert(all_); + testSellFlagBasic(all_); + testDirectToDirectPath(all_); + testDirectToDirectPath(all_ - fixAMMv1_1 - fixAMMv1_3); + testRequireAuth(all_); + testMissingAuth(all_); } void @@ -3516,15 +3517,11 @@ private: testFlow() { using namespace jtx; - // For now, just disable SAV entirely, which locks in the small Number - // mantissas in the transaction engine - FeatureBitset const all{ - testableAmendments() - featureSingleAssetVault - featureLendingProtocol}; - testFalseDry(all); - testBookStep(all); - testTransferRateNoOwnerFee(all); - testTransferRateNoOwnerFee(all - fixAMMv1_1 - fixAMMv1_3); + testFalseDry(all_); + testBookStep(all_); + testTransferRateNoOwnerFee(all_); + testTransferRateNoOwnerFee(all_ - fixAMMv1_1 - fixAMMv1_3); testLimitQuality(); testXRPPathLoop(); } @@ -3533,34 +3530,22 @@ private: testCrossingLimits() { using namespace jtx; - // For now, just disable SAV entirely, which locks in the small Number - // mantissas in the transaction engine - FeatureBitset const all{ - testableAmendments() - featureSingleAssetVault - featureLendingProtocol}; - testStepLimit(all); - testStepLimit(all - fixAMMv1_1 - fixAMMv1_3); + testStepLimit(all_); + testStepLimit(all_ - fixAMMv1_1 - fixAMMv1_3); } void testDeliverMin() { using namespace jtx; - // For now, just disable SAV entirely, which locks in the small Number - // mantissas in the transaction engine - FeatureBitset const all{ - testableAmendments() - featureSingleAssetVault - featureLendingProtocol}; - testConvertAllOfAnAsset(all); - testConvertAllOfAnAsset(all - fixAMMv1_1 - fixAMMv1_3); + testConvertAllOfAnAsset(all_); + testConvertAllOfAnAsset(all_ - fixAMMv1_1 - fixAMMv1_3); } void testDepositAuth() { - // For now, just disable SAV entirely, which locks in the small Number - // mantissas in the transaction engine - FeatureBitset const all{ - jtx::testableAmendments() - featureSingleAssetVault - featureLendingProtocol}; - testPayment(all); + testPayment(all_); testPayIOU(); } @@ -3568,13 +3553,9 @@ private: testFreeze() { using namespace test::jtx; - // For now, just disable SAV entirely, which locks in the small Number - // mantissas in the transaction engine - FeatureBitset const sa{ - testableAmendments() - featureSingleAssetVault - featureLendingProtocol}; - testRippleState(sa); - testGlobalFreeze(sa); - testOffersWhenFrozen(sa); + testRippleState(all_); + testGlobalFreeze(all_); + testOffersWhenFrozen(all_); } void diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index 17f959ae3c..64972c24ab 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -2625,10 +2625,6 @@ private: using namespace jtx; using namespace std::chrono; - // For now, just disable SAV entirely, which locks in the small Number - // mantissas - features = features - featureSingleAssetVault - featureLendingProtocol; - // Auction slot initially is owned by AMM creator, who pays 0 price. // Bid 110 tokens. Pay bidMin. @@ -3337,11 +3333,6 @@ private: testcase("Basic Payment"); using namespace jtx; - // For now, just disable SAV entirely, which locks in the small Number - // mantissas - features = - features - featureSingleAssetVault - featureLendingProtocol - featureLendingProtocol; - // Payment 100USD for 100XRP. // Force one path with tfNoRippleDirect. testAMM( diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index c8a6e813de..432dccce61 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -4767,109 +4767,119 @@ class Invariants_test : public beast::unit_test::Suite std::vector values; }; - NumberMantissaScaleGuard const g{MantissaRange::MantissaScale::Large}; - - auto makeDelta = [&vaultAsset](Number const& n) -> ValidVault::DeltaInfo { - return {.delta = n, .scale = scale(n, vaultAsset.raw())}; - }; - - auto const testCases = std::vector{ - { - .name = "No values", - .expectedMinScale = 0, - .values = {}, - }, - { - .name = "Mixed integer and Number values", - .expectedMinScale = -15, - .values = {makeDelta(1), makeDelta(-1), makeDelta(Number{10, -1})}, - }, - { - .name = "Mixed scales", - .expectedMinScale = -17, - .values = - {makeDelta(Number{1, -2}), makeDelta(Number{5, -3}), makeDelta(Number{3, -2})}, - }, - { - .name = "Equal scales", - .expectedMinScale = -16, - .values = - {makeDelta(Number{1, -1}), makeDelta(Number{5, -1}), makeDelta(Number{1, -1})}, - }, - { - .name = "Mixed mantissa sizes", - .expectedMinScale = -12, - .values = - {makeDelta(Number{1}), - makeDelta(Number{1234, -3}), - makeDelta(Number{12345, -6}), - makeDelta(Number{123, 1})}, - }, - }; - - for (auto const& tc : testCases) + for (auto const mantissaScale : { + MantissaRange::MantissaScale::LargeLegacy, + MantissaRange::MantissaScale::Large, + }) { - testcase("vault computeCoarsestScale: " + tc.name); + NumberMantissaScaleGuard const g{mantissaScale}; - auto const actualScale = ValidVault::computeCoarsestScale(tc.values); + auto makeDelta = [&vaultAsset](Number const& n) -> ValidVault::DeltaInfo { + return {.delta = n, .scale = scale(n, vaultAsset.raw())}; + }; - BEAST_EXPECTS( - actualScale == tc.expectedMinScale, - "expected: " + std::to_string(tc.expectedMinScale) + - ", actual: " + std::to_string(actualScale)); - for (auto const& num : tc.values) - { - // None of these scales are far enough apart that rounding the - // values would lose information, so check that the rounded - // value matches the original. - auto const actualRounded = roundToAsset(vaultAsset, num.delta, actualScale); - BEAST_EXPECTS( - actualRounded == num.delta, - "number " + to_string(num.delta) + " rounded to scale " + - std::to_string(actualScale) + " is " + to_string(actualRounded)); - } - } - - auto const testCases2 = std::vector{ - { - .name = "False equivalence", - .expectedMinScale = -15, - .values = - { - makeDelta(Number{1234567890123456789, -18}), - makeDelta(Number{12345, -4}), - makeDelta(Number{1}), - }, - }, - }; - - // Unlike the first set of test cases, the values in these test could - // look equivalent if using the wrong scale. - for (auto const& tc : testCases2) - { - testcase("vault computeCoarsestScale: " + tc.name); - - auto const actualScale = ValidVault::computeCoarsestScale(tc.values); - - BEAST_EXPECTS( - actualScale == tc.expectedMinScale, - "expected: " + std::to_string(tc.expectedMinScale) + - ", actual: " + std::to_string(actualScale)); - std::optional first; - Number firstRounded; - for (auto const& num : tc.values) - { - if (!first) + auto const testCases = std::vector{ { - first = num.delta; - firstRounded = roundToAsset(vaultAsset, num.delta, actualScale); - continue; - } - auto const numRounded = roundToAsset(vaultAsset, num.delta, actualScale); + .name = "No values", + .expectedMinScale = 0, + .values = {}, + }, + { + .name = "Mixed integer and Number values", + .expectedMinScale = -15, + .values = {makeDelta(1), makeDelta(-1), makeDelta(Number{10, -1})}, + }, + { + .name = "Mixed scales", + .expectedMinScale = -17, + .values = + {makeDelta(Number{1, -2}), + makeDelta(Number{5, -3}), + makeDelta(Number{3, -2})}, + }, + { + .name = "Equal scales", + .expectedMinScale = -16, + .values = + {makeDelta(Number{1, -1}), + makeDelta(Number{5, -1}), + makeDelta(Number{1, -1})}, + }, + { + .name = "Mixed mantissa sizes", + .expectedMinScale = -12, + .values = + {makeDelta(Number{1}), + makeDelta(Number{1234, -3}), + makeDelta(Number{12345, -6}), + makeDelta(Number{123, 1})}, + }, + }; + + for (auto const& tc : testCases) + { + testcase("vault computeCoarsestScale: " + tc.name); + + auto const actualScale = ValidVault::computeCoarsestScale(tc.values); + BEAST_EXPECTS( - numRounded != firstRounded, - "at a scale of " + std::to_string(actualScale) + " " + to_string(num.delta) + - " == " + to_string(*first)); + actualScale == tc.expectedMinScale, + "expected: " + std::to_string(tc.expectedMinScale) + + ", actual: " + std::to_string(actualScale)); + for (auto const& num : tc.values) + { + // None of these scales are far enough apart that rounding the + // values would lose information, so check that the rounded + // value matches the original. + auto const actualRounded = roundToAsset(vaultAsset, num.delta, actualScale); + BEAST_EXPECTS( + actualRounded == num.delta, + "number " + to_string(num.delta) + " rounded to scale " + + std::to_string(actualScale) + " is " + to_string(actualRounded)); + } + } + + auto const testCases2 = std::vector{ + { + .name = "False equivalence", + .expectedMinScale = -15, + .values = + { + makeDelta(Number{1234567890123456789, -18}), + makeDelta(Number{12345, -4}), + makeDelta(Number{1}), + }, + }, + }; + + // Unlike the first set of test cases, the values in these test could + // look equivalent if using the wrong scale. + for (auto const& tc : testCases2) + { + testcase("vault computeCoarsestScale: " + tc.name); + + auto const actualScale = ValidVault::computeCoarsestScale(tc.values); + + BEAST_EXPECTS( + actualScale == tc.expectedMinScale, + "expected: " + std::to_string(tc.expectedMinScale) + + ", actual: " + std::to_string(actualScale)); + std::optional first; + Number firstRounded; + for (auto const& num : tc.values) + { + if (!first) + { + first = num.delta; + firstRounded = roundToAsset(vaultAsset, num.delta, actualScale); + continue; + } + auto const numRounded = roundToAsset(vaultAsset, num.delta, actualScale); + BEAST_EXPECTS( + numRounded != firstRounded, + "at a scale of " + std::to_string(actualScale) + " " + + to_string(num.delta) + " == " + to_string(*first)); + } } } } diff --git a/src/test/basics/IOUAmount_test.cpp b/src/test/basics/IOUAmount_test.cpp index ef053449a5..b652d27625 100644 --- a/src/test/basics/IOUAmount_test.cpp +++ b/src/test/basics/IOUAmount_test.cpp @@ -156,8 +156,7 @@ public: BEAST_EXPECTS(result == expected, ss.str()); }; - for (auto const mantissaSize : - {MantissaRange::MantissaScale::Small, MantissaRange::MantissaScale::Large}) + for (auto const mantissaSize : MantissaRange::getAllScales()) { NumberMantissaScaleGuard const mg(mantissaSize); diff --git a/src/test/basics/Number_test.cpp b/src/test/basics/Number_test.cpp index 2a4e176ae5..26060c70e9 100644 --- a/src/test/basics/Number_test.cpp +++ b/src/test/basics/Number_test.cpp @@ -6,7 +6,10 @@ #include #include +#include + #include +#include #include #include #include @@ -19,6 +22,24 @@ namespace xrpl { class Number_test : public beast::unit_test::Suite { + using BigInt = boost::multiprecision::cpp_int; + + static std::string + fmt(BigInt const& value) + { + auto s = to_string(value); + std::string out; + int count = 0; + for (auto it = s.rbegin(); it != s.rend(); ++it) + { + if (count != 0 && count % 3 == 0 && (isdigit(*it) != 0)) + out.insert(out.begin(), '_'); + out.insert(out.begin(), *it); + ++count; + } + return out; + } + public: void testZero() @@ -178,7 +199,6 @@ public: {Number{true, 9'999'999'999'999'999'999ULL, -37, Number::Normalized{}}, Number{1'000'000'000'000'000'000, -18}, Number{false, 9'999'999'999'999'999'990ULL, -19, Number::Normalized{}}}, - {Number{Number::kMaxRep}, Number{6, -1}, Number{Number::kMaxRep / 10, 1}}, {Number{Number::kMaxRep - 1}, Number{1, 0}, Number{Number::kMaxRep}}, // Test extremes { @@ -189,16 +209,22 @@ public: Number{2, 19}, }, { - // Does not round. Mantissas are going to be > maxRep, so if + // Does not round. Mantissas are going to be > kMaxRep, so if // added together as uint64_t's, the result will overflow. // With addition using uint128_t, there's no problem. After // normalizing, the resulting mantissa ends up less than - // maxRep. + // kMaxRep. Number{false, 9'999'999'999'999'999'990ULL, 0, Number::Normalized{}}, Number{false, 9'999'999'999'999'999'990ULL, 0, Number::Normalized{}}, Number{false, 1'999'999'999'999'999'998ULL, 1, Number::Normalized{}}, }, }); + auto const cLargeLegacy = std::to_array({ + {Number{Number::kMaxRep}, Number{6, -1}, Number{Number::kMaxRep / 10, 1}}, + }); + auto const cLargeCorrected = std::to_array({ + {Number{Number::kMaxRep}, Number{6, -1}, Number{(Number::kMaxRep / 10) + 1, 1}}, + }); auto test = [this](auto const& c) { for (auto const& [x, y, z] : c) { @@ -215,6 +241,14 @@ public: else { test(cLarge); + if (scale == MantissaRange::MantissaScale::LargeLegacy) + { + test(cLargeLegacy); + } + else + { + test(cLargeCorrected); + } } { bool caught = false; @@ -835,7 +869,7 @@ public: /* auto tests = [&](auto const& cSmall, auto const& cLarge) { test(cSmall); - if (scale != MantissaRange::mantissa_scale::small) + if (scale != MantissaRange::MantissaScale::Small) test(cLarge); }; */ @@ -1266,6 +1300,7 @@ public: "9223372036854775e3"); } break; + case MantissaRange::MantissaScale::LargeLegacy: case MantissaRange::MantissaScale::Large: // Test the edges // ((exponent < -(28)) || (exponent > -(8))))) @@ -1551,11 +1586,48 @@ public: } } + void + testUpwardRoundsDown() + { + testcase << "upward rounding produces a value below exact at kMaxRep cusp"; + + NumberMantissaScaleGuard const mg{MantissaRange::MantissaScale::Large}; + NumberRoundModeGuard const rg{Number::RoundingMode::Upward}; + + constexpr std::int64_t kAValue = 1'000'000'000'000'049'863LL; + constexpr std::int64_t kBValue = 9'223'372'036'854'315'903LL; + + Number const a = kAValue; + Number const b = kBValue; + Number const product = a * b; + + // Exact reference in BigInt. + BigInt const exactProduct = BigInt(kAValue) * BigInt(kBValue); + + // What Number actually stored. + BigInt storedValue = BigInt(product.mantissa()); + for (int i = 0; i < product.exponent(); ++i) + storedValue *= 10; + + BigInt const signedDifference = storedValue - exactProduct; + + log << "\n" + << " a = " << fmt(BigInt(kAValue)) << "\n" + << " b = " << fmt(BigInt(kBValue)) << "\n" + << " exact a*b = " << fmt(exactProduct) << "\n" + << " stored = " << fmt(storedValue) << "\n" + << " stored - exact = " << fmt(signedDifference) << "\n" + << " upward = " << (signedDifference >= 0 ? "held" : "VIOLATED") << "\n"; + + BEAST_EXPECT(signedDifference >= 0); + BEAST_EXPECT(product.mantissa() == (std::numeric_limits::max() / 10) + 1); + BEAST_EXPECT(product.exponent() == 19); + } + void run() override { - for (auto const scale : - {MantissaRange::MantissaScale::Small, MantissaRange::MantissaScale::Large}) + for (auto const scale : MantissaRange::getAllScales()) { NumberMantissaScaleGuard const sg(scale); testZero(); @@ -1580,6 +1652,8 @@ public: testRounding(); testInt64(); } + // This test sets its own number range + testUpwardRoundsDown(); } }; diff --git a/src/test/protocol/STNumber_test.cpp b/src/test/protocol/STNumber_test.cpp index 4d95bc1ef7..5c9c3fd83c 100644 --- a/src/test/protocol/STNumber_test.cpp +++ b/src/test/protocol/STNumber_test.cpp @@ -280,8 +280,7 @@ struct STNumber_test : public beast::unit_test::Suite { static_assert(!std::is_convertible_v); - for (auto const scale : - {MantissaRange::MantissaScale::Small, MantissaRange::MantissaScale::Large}) + for (auto const scale : MantissaRange::getAllScales()) { NumberMantissaScaleGuard const sg(scale); testcase << to_string(Number::getMantissaScale()); diff --git a/src/test/rpc/GetAggregatePrice_test.cpp b/src/test/rpc/GetAggregatePrice_test.cpp index 1de08da205..37ecc54172 100644 --- a/src/test/rpc/GetAggregatePrice_test.cpp +++ b/src/test/rpc/GetAggregatePrice_test.cpp @@ -177,8 +177,7 @@ public: auto const all = testableAmendments(); for (auto const& feats : {all - featureSingleAssetVault - featureLendingProtocol, all}) { - for (auto const mantissaSize : - {MantissaRange::MantissaScale::Small, MantissaRange::MantissaScale::Large}) + for (auto const mantissaSize : MantissaRange::getAllScales()) { // Regardless of the features enabled, RPC is controlled by // the global mantissa size. And since it's a thread-local, From 1438bf1c6764056216bfeab5027e0eccc12fd870 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 27 May 2026 18:20:57 +0100 Subject: [PATCH 16/28] release: Bump version to 3.2.0-rc1 (#7335) --- src/libxrpl/protocol/BuildInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/protocol/BuildInfo.cpp b/src/libxrpl/protocol/BuildInfo.cpp index 5613a9366e..4e95683308 100644 --- a/src/libxrpl/protocol/BuildInfo.cpp +++ b/src/libxrpl/protocol/BuildInfo.cpp @@ -23,7 +23,7 @@ namespace { //------------------------------------------------------------------------------ // clang-format off // NOLINTNEXTLINE(readability-identifier-naming) -char const* const versionString = "3.2.0-b7" +char const* const versionString = "3.2.0-rc1" // clang-format on ; From 396d772a15a634d4a50e3f8e22e7378d1eed914d Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 27 May 2026 15:10:33 -0400 Subject: [PATCH 17/28] refactor: Enable support for `fixCleanup3_2_0` amendment (#7347) --- include/xrpl/protocol/detail/features.macro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index fd62b74d59..c99c1e5ce8 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -15,7 +15,7 @@ // Add new amendments to the top of this list. // Keep it sorted in reverse chronological order. -XRPL_FIX (Cleanup3_2_0, Supported::No, VoteBehavior::DefaultNo) +XRPL_FIX (Cleanup3_2_0, Supported::Yes, VoteBehavior::DefaultNo) XRPL_FEATURE(MPTokensV2, Supported::No, VoteBehavior::DefaultNo) XRPL_FIX (Cleanup3_1_3, Supported::Yes, VoteBehavior::DefaultYes) XRPL_FIX (BatchInnerSigs, Supported::No, VoteBehavior::DefaultNo) From 1acc42313c01e7e114333390b4485e3e176ba66f Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 27 May 2026 15:11:38 -0400 Subject: [PATCH 18/28] release: Bump version to 3.2.0-rc2 (#7348) --- src/libxrpl/protocol/BuildInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/protocol/BuildInfo.cpp b/src/libxrpl/protocol/BuildInfo.cpp index 4e95683308..2e33d03088 100644 --- a/src/libxrpl/protocol/BuildInfo.cpp +++ b/src/libxrpl/protocol/BuildInfo.cpp @@ -23,7 +23,7 @@ namespace { //------------------------------------------------------------------------------ // clang-format off // NOLINTNEXTLINE(readability-identifier-naming) -char const* const versionString = "3.2.0-rc1" +char const* const versionString = "3.2.0-rc2" // clang-format on ; From f9551ac5ca08bb851b2a17d2f5259de13c105876 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 27 May 2026 20:24:18 +0100 Subject: [PATCH 19/28] style: Run shfmt on workflows, actions and markdown bash code (#7333) --- .github/actions/build-deps/action.yml | 18 +- .github/actions/generate-version/action.yml | 10 +- .github/scripts/format-inline-bash.py | 403 ++++++++++++++++++ .github/workflows/build-nix-image.yml | 4 +- .github/workflows/check-pr-description.yml | 10 +- .github/workflows/check-pr-title.yml | 2 +- .github/workflows/on-pr.yml | 8 +- .github/workflows/pre-commit.yml | 2 +- .../workflows/reusable-build-docker-image.yml | 2 +- .../workflows/reusable-build-test-config.yml | 76 ++-- .../workflows/reusable-check-levelization.yml | 10 +- .github/workflows/reusable-check-rename.yml | 10 +- .github/workflows/reusable-clang-tidy.yml | 48 +-- .github/workflows/reusable-package.yml | 2 +- .../workflows/reusable-strategy-matrix.yml | 2 +- .pre-commit-config.yaml | 13 + BUILD.md | 6 +- cspell.config.yaml | 1 + include/xrpl/protocol_autogen/README.md | 4 +- package/README.md | 18 +- 20 files changed, 533 insertions(+), 116 deletions(-) create mode 100755 .github/scripts/format-inline-bash.py diff --git a/.github/actions/build-deps/action.yml b/.github/actions/build-deps/action.yml index 9d52be1998..0891d56dfa 100644 --- a/.github/actions/build-deps/action.yml +++ b/.github/actions/build-deps/action.yml @@ -37,12 +37,12 @@ runs: run: | echo 'Installing dependencies.' conan install \ - --profile ci \ - --build="${BUILD_OPTION}" \ - --options:host='&:tests=True' \ - --options:host='&:xrpld=True' \ - --settings:all build_type="${BUILD_TYPE}" \ - --conf:all tools.build:jobs=${BUILD_NPROC} \ - --conf:all tools.build:verbosity="${LOG_VERBOSITY}" \ - --conf:all tools.compilation:verbosity="${LOG_VERBOSITY}" \ - . + --profile ci \ + --build="${BUILD_OPTION}" \ + --options:host='&:tests=True' \ + --options:host='&:xrpld=True' \ + --settings:all build_type="${BUILD_TYPE}" \ + --conf:all tools.build:jobs=${BUILD_NPROC} \ + --conf:all tools.build:verbosity="${LOG_VERBOSITY}" \ + --conf:all tools.compilation:verbosity="${LOG_VERBOSITY}" \ + . diff --git a/.github/actions/generate-version/action.yml b/.github/actions/generate-version/action.yml index 8edb7920c6..50b3166596 100644 --- a/.github/actions/generate-version/action.yml +++ b/.github/actions/generate-version/action.yml @@ -15,7 +15,7 @@ runs: shell: bash env: VERSION: ${{ github.ref_name }} - run: echo "VERSION=${VERSION}" >> "${GITHUB_ENV}" + run: echo "VERSION=${VERSION}" >>"${GITHUB_ENV}" # When a tag is not pushed, then the version (e.g. 1.2.3-b0) is extracted # from the BuildInfo.cpp file and the shortened commit hash appended to it. @@ -28,17 +28,17 @@ runs: echo 'Extracting version from BuildInfo.cpp.' VERSION="$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')" if [[ -z "${VERSION}" ]]; then - echo 'Unable to extract version from BuildInfo.cpp.' - exit 1 + echo 'Unable to extract version from BuildInfo.cpp.' + exit 1 fi echo 'Appending shortened commit hash to version.' SHA='${{ github.sha }}' VERSION="${VERSION}+${SHA:0:7}" - echo "VERSION=${VERSION}" >> "${GITHUB_ENV}" + echo "VERSION=${VERSION}" >>"${GITHUB_ENV}" - name: Output version id: version shell: bash - run: echo "version=${VERSION}" >> "${GITHUB_OUTPUT}" + run: echo "version=${VERSION}" >>"${GITHUB_OUTPUT}" diff --git a/.github/scripts/format-inline-bash.py b/.github/scripts/format-inline-bash.py new file mode 100755 index 0000000000..423c78109c --- /dev/null +++ b/.github/scripts/format-inline-bash.py @@ -0,0 +1,403 @@ +#!/usr/bin/env python3 + +""" +Format embedded shell snippets using the shfmt hook configured in +.pre-commit-config.yaml. + +Two shapes are recognised: + +* YAML workflow/action files: literal block-scalar runs (`run: |`) and + single-line runs (`run: some command`). A single-line run is upgraded to + a `run: |` block scalar if shfmt's output spans multiple lines. + +* Markdown files: ``` ```bash ``` fenced code blocks. + +Any block that shfmt cannot parse is skipped with a warning on stderr, so +the file is left untouched and surrounding blocks still get formatted. + +For each occurrence the body is dedented, written to a temp .sh file, +formatted via `pre-commit run shfmt --files ` (falling back to +`prek`), then re-indented and written back in place. + +When invoked without arguments, every .yml/.yaml under .github/ plus every +.md file in the repo is scanned. When invoked with file arguments (the +pre-commit case), only those files are processed. +""" + +from __future__ import annotations + +import re +import shutil +import subprocess +import sys +import tempfile +from dataclasses import dataclass +from pathlib import Path +from typing import Union + +REPO = Path(__file__).resolve().parents[2] + +_HOOK_RUNNER = next((cmd for cmd in ("pre-commit", "prek") if shutil.which(cmd)), None) +if _HOOK_RUNNER is None: + sys.exit("error: neither `pre-commit` nor `prek` found on PATH") + +RUN_BLOCK_RE = re.compile(r"^(?P[ \t]*(?:- )?)run:[ \t]*\|[+-]?[ \t]*$") +RUN_INLINE_RE = re.compile( + r"^(?P[ \t]*(?:- )?)run:[ \t]+" r"(?P(?!\|[+-]?[ \t]*$)\S.*?)[ \t]*$" +) +MD_BASH_OPEN_RE = re.compile(r"^(?P[ ]{0,3})`{3}bash[ \t]*$") +MD_FENCE_CLOSE_RE = re.compile(r"^[ ]{0,3}`{3,}[ \t]*$") + + +@dataclass(frozen=True) +class BlockRun: + """A `run: |` block scalar; `body_start:body_end` slices into `lines`.""" + + body_start: int + body_end: int + body_indent: int + + +@dataclass(frozen=True) +class InlineRun: + """A single-line `run: value` at `line_idx`.""" + + line_idx: int + prefix: str + value: str + + +@dataclass(frozen=True) +class MdBashBlock: + """A markdown ``` ```bash ``` fenced code block. + + `body_start:body_end` slices into the file's lines; `open_line_idx` + points at the opening fence line. + """ + + open_line_idx: int + body_start: int + body_end: int + body_indent: int + + +RunItem = Union[BlockRun, InlineRun] + + +def _scan_block_body( + lines: list[str], body_start: int, run_col: int +) -> tuple[int | None, int]: + """Locate the body of a `run: |` block scalar starting at `body_start`. + + Returns `(body_indent, scan_end)`. `scan_end` is the line index where the + outer scanner should resume. `body_indent` is `None` when no body is + present (the scalar is empty, or the next non-blank line has indent + `<= run_col`). + """ + body_indent: int | None = None + scan_end = len(lines) + for idx in range(body_start, len(lines)): + line = lines[idx] + if line.strip() == "": + continue + indent = len(line) - len(line.lstrip(" ")) + if body_indent is None: + if indent > run_col: + body_indent = indent + else: + scan_end = idx + break + elif indent < body_indent: + scan_end = idx + break + if body_indent is not None: + while scan_end > body_start and lines[scan_end - 1].strip() == "": + scan_end -= 1 + if scan_end <= body_start: + body_indent = None + return body_indent, scan_end + + +def find_run_blocks(lines: list[str]) -> list[RunItem]: + """Return run items in document order.""" + items: list[RunItem] = [] + line_idx = 0 + while line_idx < len(lines): + line = lines[line_idx] + if block_match := RUN_BLOCK_RE.match(line): + run_col = len(block_match.group("prefix")) + body_start = line_idx + 1 + body_indent, scan_end = _scan_block_body(lines, body_start, run_col) + if body_indent is not None: + items.append( + BlockRun( + body_start=body_start, + body_end=scan_end, + body_indent=body_indent, + ) + ) + line_idx = scan_end + continue + if inline_match := RUN_INLINE_RE.match(line): + items.append( + InlineRun( + line_idx=line_idx, + prefix=inline_match.group("prefix"), + value=inline_match.group("value"), + ) + ) + line_idx += 1 + return items + + +def find_md_bash_blocks(lines: list[str]) -> list[MdBashBlock]: + """Return ``` ```bash ``` fenced code blocks in document order.""" + blocks: list[MdBashBlock] = [] + line_idx = 0 + while line_idx < len(lines): + open_match = MD_BASH_OPEN_RE.match(lines[line_idx]) + if not open_match: + line_idx += 1 + continue + body_start = line_idx + 1 + close_idx = next( + ( + j + for j in range(body_start, len(lines)) + if MD_FENCE_CLOSE_RE.match(lines[j]) + ), + None, + ) + if close_idx is None: + line_idx = body_start + continue + body = lines[body_start:close_idx] + non_blank = [b for b in body if b.strip()] + body_indent = ( + min(len(b) - len(b.lstrip(" ")) for b in non_blank) + if non_blank + else len(open_match.group("indent")) + ) + blocks.append( + MdBashBlock( + open_line_idx=line_idx, + body_start=body_start, + body_end=close_idx, + body_indent=body_indent, + ) + ) + line_idx = close_idx + 1 + return blocks + + +def dedent(lines: list[str], n: int) -> list[str]: + pad = " " * n + return [ + ( + "" + if line.strip() == "" + else (line[n:] if line.startswith(pad) else line.lstrip(" ")) + ) + for line in lines + ] + + +def reindent(lines: list[str], n: int) -> list[str]: + pad = " " * n + return [pad + line if line else "" for line in lines] + + +_SHFMT_ERR_RE = re.compile(r"\.sh:\d+:\d+:\s") +_GHA_EXPR_RE = re.compile(r"\$\{\{.*?\}\}", re.DOTALL) +_GHA_PLACEHOLDER_RE = re.compile(r"__GHA_EXPR_(\d+)__") + + +def _encode_gha_exprs(text: str) -> tuple[str, list[str]]: + """Replace `${{ ... }}` expressions with bash-safe placeholder identifiers.""" + exprs: list[str] = [] + + def repl(match: re.Match[str]) -> str: + exprs.append(match.group(0)) + return f"__GHA_EXPR_{len(exprs) - 1}__" + + return _GHA_EXPR_RE.sub(repl, text), exprs + + +def _decode_gha_exprs(text: str, exprs: list[str]) -> str: + """Restore `${{ ... }}` expressions from placeholder identifiers.""" + return _GHA_PLACEHOLDER_RE.sub(lambda m: exprs[int(m.group(1))], text) + + +def shfmt_via_hook(tmp_path: Path) -> tuple[bool, str]: + # `${{ ... }}` is not valid shell, so swap it for a placeholder identifier + # that shfmt can parse, then restore it after formatting. + encoded, exprs = _encode_gha_exprs(tmp_path.read_text()) + if exprs: + tmp_path.write_text(encoded) + res = subprocess.run( + [_HOOK_RUNNER, "run", "shfmt", "--files", str(tmp_path)], + cwd=REPO, + capture_output=True, + text=True, + ) + output = res.stdout + res.stderr + # shfmt emits parse errors as "::: ". + parse_err = bool(_SHFMT_ERR_RE.search(output)) + # A non-zero exit that is neither a parse error nor pre-commit's "I had + # to modify files" signal means the hook itself failed to run (missing + # binary, install failure, bad config, ...). Surface that loudly rather + # than silently treating it as a no-op. + if ( + res.returncode != 0 + and not parse_err + and "files were modified by this hook" not in output + ): + sys.exit( + f"error: `{_HOOK_RUNNER} run shfmt` failed with exit {res.returncode}:\n{output}" + ) + if exprs and not parse_err: + tmp_path.write_text(_decode_gha_exprs(tmp_path.read_text(), exprs)) + return not parse_err, output + + +def _skip(path: Path, where: int, kind: str, output: str) -> None: + print( + f" shfmt could not parse {kind} at {path}:{where + 1} — skipped", + file=sys.stderr, + ) + print(f" {output.strip()}", file=sys.stderr) + + +def process_yaml_file(path: Path, tmp_path: Path) -> int: + text = path.read_text() + had_nl = text.endswith("\n") + lines = text.split("\n") + if had_nl: + lines = lines[:-1] + items = find_run_blocks(lines) + if not items: + return 0 + changed = 0 + # Process in reverse so earlier indices remain valid as we splice. + for item in reversed(items): + if isinstance(item, BlockRun): + body = lines[item.body_start : item.body_end] + tmp_path.write_text("\n".join(dedent(body, item.body_indent)) + "\n") + ok, output = shfmt_via_hook(tmp_path) + if not ok: + _skip(path, item.body_start, "block", output) + continue + formatted = tmp_path.read_text().rstrip("\n") + new_body = reindent(formatted.split("\n"), item.body_indent) + if new_body != body: + lines[item.body_start : item.body_end] = new_body + changed += 1 + else: + tmp_path.write_text(item.value + "\n") + ok, output = shfmt_via_hook(tmp_path) + if not ok: + _skip(path, item.line_idx, "inline run", output) + continue + formatted = tmp_path.read_text().rstrip("\n") + if formatted == item.value: + continue + formatted_lines = formatted.split("\n") + if len(formatted_lines) == 1: + lines[item.line_idx] = f"{item.prefix}run: {formatted}" + else: + body_indent = len(item.prefix) + 2 + lines[item.line_idx : item.line_idx + 1] = [ + f"{item.prefix}run: |", + *reindent(formatted_lines, body_indent), + ] + changed += 1 + new_text = "\n".join(lines) + ("\n" if had_nl else "") + if new_text != text: + path.write_text(new_text) + return changed + + +def process_md_file(path: Path, tmp_path: Path) -> int: + text = path.read_text() + had_nl = text.endswith("\n") + lines = text.split("\n") + if had_nl: + lines = lines[:-1] + blocks = find_md_bash_blocks(lines) + if not blocks: + return 0 + changed = 0 + for block in reversed(blocks): + body = lines[block.body_start : block.body_end] + tmp_path.write_text("\n".join(dedent(body, block.body_indent)) + "\n") + ok, output = shfmt_via_hook(tmp_path) + if not ok: + _skip(path, block.open_line_idx, "```bash block", output) + continue + formatted = tmp_path.read_text().rstrip("\n") + formatted_lines = formatted.split("\n") if formatted else [] + new_body = reindent(formatted_lines, block.body_indent) + if new_body != body: + lines[block.body_start : block.body_end] = new_body + changed += 1 + new_text = "\n".join(lines) + ("\n" if had_nl else "") + if new_text != text: + path.write_text(new_text) + return changed + + +def process_file(path: Path, tmp_path: Path) -> int: + if path.suffix in (".yml", ".yaml"): + return process_yaml_file(path, tmp_path) + if path.suffix == ".md": + return process_md_file(path, tmp_path) + return 0 + + +def gather_files(argv: list[str]) -> list[Path]: + """Return YAML workflow/action files and markdown files that we should + process — either the paths in `argv` or, when `argv` is empty, every + such file in the repo (skipping `external/`).""" + if argv: + candidates: list[Path] = [ + (REPO / a).resolve() if not Path(a).is_absolute() else Path(a) for a in argv + ] + else: + gh = REPO / ".github" + candidates = [ + *gh.rglob("*.yml"), + *gh.rglob("*.yaml"), + *( + p + for p in REPO.rglob("*.md") + if "external" not in p.relative_to(REPO).parts + ), + ] + return sorted( + p + for p in candidates + if p.exists() + and ( + (p.suffix in (".yml", ".yaml") and ".github" in p.parts) + or p.suffix == ".md" + ) + ) + + +def main(argv: list[str]) -> int: + files = gather_files(argv) + if not files: + return 0 + with tempfile.TemporaryDirectory(prefix="format-inline-bash-") as tmpdir: + tmp_path = Path(tmpdir) / "shfmt.sh" + total = 0 + for f in files: + n = process_file(f, tmp_path) + if n: + print(f"{f.relative_to(REPO)}: reformatted {n} block(s)") + total += n + return 1 if total else 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/.github/workflows/build-nix-image.yml b/.github/workflows/build-nix-image.yml index edd35132fa..bae4cfd437 100644 --- a/.github/workflows/build-nix-image.yml +++ b/.github/workflows/build-nix-image.yml @@ -100,8 +100,8 @@ jobs: - name: Create multi-arch manifests run: | - for tag in $(jq -cr '.tags[]' <<< "$DOCKER_METADATA_OUTPUT_JSON"); do - docker buildx imagetools create -t "$tag" "${tag}-amd64" "${tag}-arm64" + for tag in $(jq -cr '.tags[]' <<<"$DOCKER_METADATA_OUTPUT_JSON"); do + docker buildx imagetools create -t "$tag" "${tag}-amd64" "${tag}-arm64" done - name: Inspect image diff --git a/.github/workflows/check-pr-description.yml b/.github/workflows/check-pr-description.yml index f6eee50291..3f345a9dcb 100644 --- a/.github/workflows/check-pr-description.yml +++ b/.github/workflows/check-pr-description.yml @@ -20,11 +20,11 @@ jobs: env: PR_BODY: ${{ github.event.pull_request.body }} if: ${{ github.event_name == 'pull_request' }} - run: printenv PR_BODY > pr_body.md + run: printenv PR_BODY >pr_body.md - name: Check PR description differs from template if: ${{ github.event_name == 'pull_request' }} - run: > - python .github/scripts/check-pr-description.py - --template-file .github/pull_request_template.md - --pr-body-file pr_body.md + run: | + python .github/scripts/check-pr-description.py \ + --template-file .github/pull_request_template.md \ + --pr-body-file pr_body.md diff --git a/.github/workflows/check-pr-title.yml b/.github/workflows/check-pr-title.yml index 5631950df6..79962e0620 100644 --- a/.github/workflows/check-pr-title.yml +++ b/.github/workflows/check-pr-title.yml @@ -11,4 +11,4 @@ on: jobs: check_title: if: ${{ github.event.pull_request.draft != true }} - uses: XRPLF/actions/.github/workflows/check-pr-title.yml@291206777251b4d493641b5afbdf7c23009d2988 + uses: XRPLF/actions/.github/workflows/check-pr-title.yml@cba1f0891650baf1a9c88624dc2d72573be2eb81 diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index ca715e0376..db3c8667e5 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -98,7 +98,7 @@ jobs: READY: ${{ contains(github.event.pull_request.labels.*.name, 'Ready to merge') }} MERGE: ${{ github.event_name == 'merge_group' }} run: | - echo "go=${{ (env.DRAFT != 'true' && env.READY == 'true') || env.FILES == 'true' || env.MERGE == 'true' }}" >> "${GITHUB_OUTPUT}" + echo "go=${{ (env.DRAFT != 'true' && env.READY == 'true') || env.FILES == 'true' || env.MERGE == 'true' }}" >>"${GITHUB_OUTPUT}" cat "${GITHUB_OUTPUT}" outputs: go: ${{ steps.go.outputs.go == 'true' }} @@ -168,9 +168,9 @@ jobs: PR_URL: ${{ github.event.pull_request.html_url }} run: | gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \ - /repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \ - -F "client_payload[ref]=${{ needs.upload-recipe.outputs.recipe_ref }}" \ - -F "client_payload[pr_url]=${PR_URL}" + /repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \ + -F "client_payload[ref]=${{ needs.upload-recipe.outputs.recipe_ref }}" \ + -F "client_payload[pr_url]=${PR_URL}" passed: if: failure() || cancelled() diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 2f15b82266..de6a4f40b4 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -14,7 +14,7 @@ on: jobs: # Call the workflow in the XRPLF/actions repo that runs the pre-commit hooks. run-hooks: - uses: XRPLF/actions/.github/workflows/pre-commit.yml@5e942d61bf32f7557a7c159cfac4712a687b3e3a + uses: XRPLF/actions/.github/workflows/pre-commit.yml@cba1f0891650baf1a9c88624dc2d72573be2eb81 with: runs_on: ubuntu-latest container: '{ "image": "ghcr.io/xrplf/ci/tools-rippled-pre-commit:sha-41ec7c1" }' diff --git a/.github/workflows/reusable-build-docker-image.yml b/.github/workflows/reusable-build-docker-image.yml index 5b555b713f..c3795e56fa 100644 --- a/.github/workflows/reusable-build-docker-image.yml +++ b/.github/workflows/reusable-build-docker-image.yml @@ -53,7 +53,7 @@ jobs: env: PLATFORM: ${{ inputs.platform }} run: | - echo "arch=${PLATFORM##*/}" >> $GITHUB_OUTPUT + echo "arch=${PLATFORM##*/}" >>$GITHUB_OUTPUT - name: Set up Docker Buildx uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index 4f9b926e98..31457bb892 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -113,7 +113,7 @@ jobs: - name: Set ccache log file if: ${{ inputs.ccache_enabled && runner.debug == '1' }} - run: echo "CCACHE_LOGFILE=${{ runner.temp }}/ccache.log" >> "${GITHUB_ENV}" + run: echo "CCACHE_LOGFILE=${{ runner.temp }}/ccache.log" >>"${GITHUB_ENV}" - name: Print build environment uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574 @@ -146,11 +146,11 @@ jobs: CMAKE_ARGS: ${{ inputs.cmake_args }} run: | cmake \ - -G '${{ runner.os == 'Windows' && 'Visual Studio 17 2022' || 'Ninja' }}' \ - -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ - -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ - ${CMAKE_ARGS} \ - .. + -G '${{ runner.os == 'Windows' && 'Visual Studio 17 2022' || 'Ninja' }}' \ + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ + -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ + ${CMAKE_ARGS} \ + .. - name: Check protocol autogen files are up-to-date working-directory: ${{ env.BUILD_DIR }} @@ -172,10 +172,10 @@ jobs: cmake --build . --target code_gen DIFF=$(git -C .. status --porcelain -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen) if [ -n "${DIFF}" ]; then - echo "::error::Generated protocol files are out of date" - git -C .. diff -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen - echo "${MESSAGE}" - exit 1 + echo "::error::Generated protocol files are out of date" + git -C .. diff -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen + echo "${MESSAGE}" + exit 1 fi - name: Build the binary @@ -186,18 +186,18 @@ jobs: CMAKE_TARGET: ${{ inputs.cmake_target }} run: | cmake \ - --build . \ - --config "${BUILD_TYPE}" \ - --parallel "${BUILD_NPROC}" \ - --target "${CMAKE_TARGET}" + --build . \ + --config "${BUILD_TYPE}" \ + --parallel "${BUILD_NPROC}" \ + --target "${CMAKE_TARGET}" - name: Show ccache statistics if: ${{ inputs.ccache_enabled }} run: | ccache --show-stats -vv if [ '${{ runner.debug }}' = '1' ]; then - cat "${CCACHE_LOGFILE}" - curl ${CCACHE_REMOTE_STORAGE%|*}/status || true + cat "${CCACHE_LOGFILE}" + curl ${CCACHE_REMOTE_STORAGE%|*}/status || true fi - name: Upload the binary (Linux) @@ -214,7 +214,7 @@ jobs: working-directory: ${{ env.BUILD_DIR }} run: | set -o pipefail - ./xrpld --definitions | python3 -m json.tool > server_definitions.json + ./xrpld --definitions | python3 -m json.tool >server_definitions.json - name: Upload server definitions if: ${{ github.event.repository.visibility == 'public' && inputs.config_name == 'debian-bookworm-gcc-13-amd64-release' }} @@ -231,10 +231,10 @@ jobs: run: | ldd ./xrpld if [ "$(ldd ./xrpld | grep -E '(libstdc\+\+|libgcc)' | wc -l)" -eq 0 ]; then - echo 'The binary is statically linked.' + echo 'The binary is statically linked.' else - echo 'The binary is dynamically linked.' - exit 1 + echo 'The binary is dynamically linked.' + exit 1 fi - name: Verify presence of instrumentation (Linux) @@ -250,12 +250,12 @@ jobs: run: | ASAN_OPTS="include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-asan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp" if [[ "${CONFIG_NAME}" == *gcc* ]]; then - ASAN_OPTS="${ASAN_OPTS}:alloc_dealloc_mismatch=0" + ASAN_OPTS="${ASAN_OPTS}:alloc_dealloc_mismatch=0" fi - echo "ASAN_OPTIONS=${ASAN_OPTS}" >> ${GITHUB_ENV} - echo "TSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-tsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV} - echo "UBSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV} - echo "LSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-lsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV} + echo "ASAN_OPTIONS=${ASAN_OPTS}" >>${GITHUB_ENV} + echo "TSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-tsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >>${GITHUB_ENV} + echo "UBSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >>${GITHUB_ENV} + echo "LSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-lsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >>${GITHUB_ENV} - name: Run the separate tests if: ${{ !inputs.build_only }} @@ -266,9 +266,9 @@ jobs: PARALLELISM: ${{ runner.os == 'Windows' && '1' || steps.nproc.outputs.nproc }} run: | ctest \ - --output-on-failure \ - -C "${BUILD_TYPE}" \ - -j "${PARALLELISM}" + --output-on-failure \ + -C "${BUILD_TYPE}" \ + -j "${PARALLELISM}" - name: Run the embedded tests if: ${{ !inputs.build_only }} @@ -278,7 +278,7 @@ jobs: run: | set -o pipefail # Coverage builds are slower due to instrumentation; use fewer parallel jobs to avoid flakiness - [ "$COVERAGE_ENABLED" = "true" ] && BUILD_NPROC=$(( BUILD_NPROC - 2 )) + [ "$COVERAGE_ENABLED" = "true" ] && BUILD_NPROC=$((BUILD_NPROC - 2)) ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log - name: Show test failure summary @@ -287,19 +287,19 @@ jobs: WORKING_DIR: ${{ runner.os == 'Windows' && format('{0}\{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }} run: | if [ ! -d "${WORKING_DIR}" ]; then - echo "Working directory '${WORKING_DIR}' does not exist." - exit 0 + echo "Working directory '${WORKING_DIR}' does not exist." + exit 0 fi cd "${WORKING_DIR}" if [ ! -f unittest.log ]; then - echo "unittest.log not found; embedded tests may not have run." - exit 0 + echo "unittest.log not found; embedded tests may not have run." + exit 0 fi if ! grep -E "failed" unittest.log; then - echo "Log present but no failure lines found in unittest.log." + echo "Log present but no failure lines found in unittest.log." fi - name: Debug failure (Linux) if: ${{ failure() && runner.os == 'Linux' && !inputs.build_only }} @@ -317,10 +317,10 @@ jobs: BUILD_TYPE: ${{ inputs.build_type }} run: | cmake \ - --build . \ - --config "${BUILD_TYPE}" \ - --parallel "${BUILD_NPROC}" \ - --target coverage + --build . \ + --config "${BUILD_TYPE}" \ + --parallel "${BUILD_NPROC}" \ + --target coverage - name: Upload coverage report if: ${{ github.repository == 'XRPLF/rippled' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }} diff --git a/.github/workflows/reusable-check-levelization.yml b/.github/workflows/reusable-check-levelization.yml index 4efe3e1138..b5d57a177a 100644 --- a/.github/workflows/reusable-check-levelization.yml +++ b/.github/workflows/reusable-check-levelization.yml @@ -38,9 +38,9 @@ jobs: run: | DIFF=$(git status --porcelain) if [ -n "${DIFF}" ]; then - # Print the differences to give the contributor a hint about what to - # expect when running levelization on their own machine. - git diff - echo "${MESSAGE}" - exit 1 + # Print the differences to give the contributor a hint about what to + # expect when running levelization on their own machine. + git diff + echo "${MESSAGE}" + exit 1 fi diff --git a/.github/workflows/reusable-check-rename.yml b/.github/workflows/reusable-check-rename.yml index 56a1a3e637..7aa5b80594 100644 --- a/.github/workflows/reusable-check-rename.yml +++ b/.github/workflows/reusable-check-rename.yml @@ -48,9 +48,9 @@ jobs: run: | DIFF=$(git status --porcelain) if [ -n "${DIFF}" ]; then - # Print the differences to give the contributor a hint about what to - # expect when running the renaming scripts on their own machine. - git diff - echo "${MESSAGE}" - exit 1 + # Print the differences to give the contributor a hint about what to + # expect when running the renaming scripts on their own machine. + git diff + echo "${MESSAGE}" + exit 1 fi diff --git a/.github/workflows/reusable-clang-tidy.yml b/.github/workflows/reusable-clang-tidy.yml index e01a50cf6d..8be1db5fb2 100644 --- a/.github/workflows/reusable-clang-tidy.yml +++ b/.github/workflows/reusable-clang-tidy.yml @@ -70,13 +70,13 @@ jobs: working-directory: ${{ env.BUILD_DIR }} run: | cmake \ - -G 'Ninja' \ - -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ - -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ - -Dtests=ON \ - -Dwerr=ON \ - -Dxrpld=ON \ - .. + -G 'Ninja' \ + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ + -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ + -Dtests=ON \ + -Dwerr=ON \ + -Dxrpld=ON \ + .. # clang-tidy needs headers generated from proto files - name: Build libxrpl.libpb @@ -133,7 +133,7 @@ jobs: - name: Write issue header if: ${{ steps.run_clang_tidy.outcome != 'success' }} run: | - cat > "${ISSUE_FILE}" <"${ISSUE_FILE}" < filtered-output.txt || true + # Extract lines containing 'error:', 'warning:', or 'note:' + grep -E '(error:|warning:|note:)' "${OUTPUT_FILE}" >filtered-output.txt || true - # If filtered output is empty, use original (might be a different error format) - if [ ! -s filtered-output.txt ]; then - cp "${OUTPUT_FILE}" filtered-output.txt - fi + # If filtered output is empty, use original (might be a different error format) + if [ ! -s filtered-output.txt ]; then + cp "${OUTPUT_FILE}" filtered-output.txt + fi - # Truncate if too large - head -c 60000 filtered-output.txt >> "${ISSUE_FILE}" - if [ "$(wc -c < filtered-output.txt)" -gt 60000 ]; then - echo "" >> "${ISSUE_FILE}" - echo "... (output truncated, see artifacts for full output)" >> "${ISSUE_FILE}" - fi + # Truncate if too large + head -c 60000 filtered-output.txt >>"${ISSUE_FILE}" + if [ "$(wc -c >"${ISSUE_FILE}" + echo "... (output truncated, see artifacts for full output)" >>"${ISSUE_FILE}" + fi - rm filtered-output.txt + rm filtered-output.txt else - echo "No output file found" >> "${ISSUE_FILE}" + echo "No output file found" >>"${ISSUE_FILE}" fi - name: Append issue footer if: ${{ steps.run_clang_tidy.outcome != 'success' }} run: | - cat >> "${ISSUE_FILE}" <>"${ISSUE_FILE}" <> "${GITHUB_OUTPUT}" + ./generate.py --packaging --config=linux.json >>"${GITHUB_OUTPUT}" generate-version: runs-on: ubuntu-latest diff --git a/.github/workflows/reusable-strategy-matrix.yml b/.github/workflows/reusable-strategy-matrix.yml index b1232a138f..62d65ad3fa 100644 --- a/.github/workflows/reusable-strategy-matrix.yml +++ b/.github/workflows/reusable-strategy-matrix.yml @@ -42,4 +42,4 @@ jobs: env: GENERATE_CONFIG: ${{ inputs.os != '' && format('--config={0}.json', inputs.os) || '' }} GENERATE_OPTION: ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} - run: ./generate.py ${GENERATE_OPTION} ${GENERATE_CONFIG} >> "${GITHUB_OUTPUT}" + run: ./generate.py ${GENERATE_OPTION} ${GENERATE_CONFIG} >>"${GITHUB_OUTPUT}" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 23441c9dde..c9dec89435 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -66,6 +66,19 @@ repos: - id: shfmt args: [--write, --indent=4, --case-indent=true] + - repo: local + hooks: + - id: format-inline-bash-workflows + name: "format `run:` blocks in workflows/actions" + entry: ./.github/scripts/format-inline-bash.py + language: python + files: ^\.github/(workflows|actions)/.*\.ya?ml$ + - id: format-inline-bash-markdown + name: "format ```bash blocks in markdown" + entry: ./.github/scripts/format-inline-bash.py + language: python + files: \.md$ + - repo: https://github.com/streetsidesoftware/cspell-cli rev: 4643f154907327ee0a2c7038f0296e0dd77d9776 # frozen: v10.0.0 hooks: diff --git a/BUILD.md b/BUILD.md index a1163dbfcc..1d3fc8f774 100644 --- a/BUILD.md +++ b/BUILD.md @@ -151,8 +151,8 @@ git init git remote add origin git@github.com:XRPLF/conan-center-index.git git sparse-checkout init for recipe in "${recipes[@]}"; do - echo "Checking out recipe '${recipe}'..." - git sparse-checkout add recipes/${recipe} + echo "Checking out recipe '${recipe}'..." + git sparse-checkout add recipes/${recipe} done git fetch origin master git checkout master @@ -180,7 +180,7 @@ the new recipe will be automatically pulled from the official Conan Center. If you see an error similar to the following after running `conan profile show`: -```bash +```text ERROR: Invalid setting '17' is not a valid 'settings.compiler.version' value. Possible values are ['5.0', '5.1', '6.0', '6.1', '7.0', '7.3', '8.0', '8.1', '9.0', '9.1', '10.0', '11.0', '12.0', '13', '13.0', '13.1', '14', '14.0', '15', diff --git a/cspell.config.yaml b/cspell.config.yaml index f26268c804..ed936941e4 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -93,6 +93,7 @@ words: - daria - dcmake - dearmor + - dedented - deleteme - demultiplexer - deserializaton diff --git a/include/xrpl/protocol_autogen/README.md b/include/xrpl/protocol_autogen/README.md index 860ed1a242..608ffed085 100644 --- a/include/xrpl/protocol_autogen/README.md +++ b/include/xrpl/protocol_autogen/README.md @@ -15,8 +15,8 @@ Generation requires a one-time setup step to create a virtual environment and install Python dependencies, followed by running the generation target: ```bash -cmake --build . --target setup_code_gen # create venv and install dependencies (once) -cmake --build . --target code_gen # generate code +cmake --build . --target setup_code_gen # create venv and install dependencies (once) +cmake --build . --target code_gen # generate code ``` By default, `CODEGEN_VENV_DIR` points to `.venv` in the project root. The diff --git a/package/README.md b/package/README.md index 2089e32e64..867ca273b4 100644 --- a/package/README.md +++ b/package/README.md @@ -74,10 +74,10 @@ VERSION=2.4.0-local PKG_RELEASE=1 docker run --rm \ - -v "$(pwd):/src" \ - -w /src \ - "$IMAGE" \ - ./package/build_pkg.sh --pkg-version "$VERSION" --pkg-release "$PKG_RELEASE" + -v "$(pwd):/src" \ + -w /src \ + "$IMAGE" \ + ./package/build_pkg.sh --pkg-version "$VERSION" --pkg-release "$PKG_RELEASE" # Output: # build/debbuild/*.deb (DEB + dbgsym .ddeb) @@ -92,12 +92,12 @@ needed, but the host toolchain replaces the pinned CI image: ```bash cmake \ - -Dxrpld=ON \ - -Dxrpld_version=2.4.0-local \ - -Dtests=OFF \ - .. + -Dxrpld=ON \ + -Dxrpld_version=2.4.0-local \ + -Dtests=OFF \ + .. -cmake --build . --target package # deb on Debian/Ubuntu, rpm on RHEL +cmake --build . --target package # deb on Debian/Ubuntu, rpm on RHEL ``` The `cmake/XrplPackaging.cmake` module defines the target only if at least one From 2f3558c610ee3d446b1c68a003442e63d4abbdf5 Mon Sep 17 00:00:00 2001 From: Bart Date: Thu, 28 May 2026 10:57:29 -0400 Subject: [PATCH 20/28] ci: Run PR title and description checks on staging and release branches (#7331) Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com> --- .github/workflows/check-pr-description.yml | 13 +++++++++++-- .github/workflows/check-pr-title.yml | 13 +++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check-pr-description.yml b/.github/workflows/check-pr-description.yml index 3f345a9dcb..ff28220171 100644 --- a/.github/workflows/check-pr-description.yml +++ b/.github/workflows/check-pr-description.yml @@ -5,8 +5,17 @@ on: types: - checks_requested pull_request: - types: [opened, edited, reopened, synchronize, ready_for_review] - branches: [develop] + types: + - opened + - edited + - reopened + - synchronize + - ready_for_review + branches: + - develop + - "release-*" + - "release/*" + - "staging/*" jobs: check_description: diff --git a/.github/workflows/check-pr-title.yml b/.github/workflows/check-pr-title.yml index 79962e0620..4b5f679df1 100644 --- a/.github/workflows/check-pr-title.yml +++ b/.github/workflows/check-pr-title.yml @@ -5,8 +5,17 @@ on: types: - checks_requested pull_request: - types: [opened, edited, reopened, synchronize, ready_for_review] - branches: [develop] + types: + - opened + - edited + - reopened + - synchronize + - ready_for_review + branches: + - develop + - "release-*" + - "release/*" + - "staging/*" jobs: check_title: From 763dd503be465e92d3ec4bf17a13b4aa366080a8 Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Fri, 29 May 2026 20:16:25 -0400 Subject: [PATCH 21/28] fix: Add zero domainID check for permissionedDomain (#7362) --- .../ledger/helpers/PermissionedDEXHelpers.cpp | 12 +++++++ .../tx/transactors/dex/OfferCreate.cpp | 6 ++++ .../tx/transactors/payment/Payment.cpp | 6 ++++ src/test/app/PermissionedDEX_test.cpp | 34 +++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/src/libxrpl/ledger/helpers/PermissionedDEXHelpers.cpp b/src/libxrpl/ledger/helpers/PermissionedDEXHelpers.cpp index 0151c5b2df..f8653f7111 100644 --- a/src/libxrpl/ledger/helpers/PermissionedDEXHelpers.cpp +++ b/src/libxrpl/ledger/helpers/PermissionedDEXHelpers.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -19,6 +21,16 @@ namespace xrpl::permissioned_dex { bool accountInDomain(ReadView const& view, AccountID const& account, Domain const& domainID) { + // Avoid constructing a zero-key PermissionedDomain keylet. + // keylet::permissionedDomain(uint256) uses the DomainID as the ledger key. + if (view.rules().enabled(fixCleanup3_2_0) && domainID == beast::kZero) + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::permissioned_dex::accountInDomain : domainID is zero"); + return false; + // LCOV_EXCL_STOP + } + auto const sleDomain = view.read(keylet::permissionedDomain(domainID)); if (!sleDomain) return false; diff --git a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp index 8bf69d25c0..f982837c5e 100644 --- a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -94,6 +94,12 @@ OfferCreate::preflight(PreflightContext const& ctx) if (tx.isFlag(tfHybrid) && !tx.isFieldPresent(sfDomainID)) return temINVALID_FLAG; + // A zero DomainID is invalid for a PermissionedDomain ledger entry because + // keylet::permissionedDomain(uint256) uses the DomainID as the ledger key. + if (auto const domainID = tx[~sfDomainID]; + ctx.rules.enabled(fixCleanup3_2_0) && domainID && *domainID == beast::kZero) + return temMALFORMED; + bool const bImmediateOrCancel(tx.isFlag(tfImmediateOrCancel)); bool const bFillOrKill(tx.isFlag(tfFillOrKill)); diff --git a/src/libxrpl/tx/transactors/payment/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp index 1848d34786..d7ad5d3b3c 100644 --- a/src/libxrpl/tx/transactors/payment/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -125,6 +125,12 @@ Payment::preflight(PreflightContext const& ctx) if (!mpTokensV2 && isDstMPT && ctx.tx.isFieldPresent(sfPaths)) return temMALFORMED; + // A zero DomainID is invalid for a PermissionedDomain ledger entry because + // keylet::permissionedDomain(uint256) uses the DomainID as the ledger key. + if (auto const domainID = tx[~sfDomainID]; + ctx.rules.enabled(fixCleanup3_2_0) && domainID && *domainID == beast::kZero) + return temMALFORMED; + bool const partialPaymentAllowed = tx.isFlag(tfPartialPayment); bool const limitQuality = tx.isFlag(tfLimitQuality); bool const defaultPathsAllowed = !tx.isFlag(tfNoRippleDirect); diff --git a/src/test/app/PermissionedDEX_test.cpp b/src/test/app/PermissionedDEX_test.cpp index c6e94d7994..a88cbaa868 100644 --- a/src/test/app/PermissionedDEX_test.cpp +++ b/src/test/app/PermissionedDEX_test.cpp @@ -197,6 +197,20 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); } + // test preflight - malformed DomainID being zero + // Only test this with fixCleanup3_2_0 enabled. Without the fix, + // an assert-enabled build can crash when Ledger::read() receives + // a zero-key PermissionedDomain keylet. + if (features[fixCleanup3_2_0]) + { + Env env(*this, features); + auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + PermissionedDEX(env); + + env(offer(bob_, XRP(10), USD(10)), Domain(uint256{}), Ter(temMALFORMED)); + env.close(); + } + // preclaim - someone outside of the domain cannot create domain offer { Env env(*this, features); @@ -396,6 +410,24 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); } + // test preflight - malformed DomainID being zero + // Only test this with fixCleanup3_2_0 enabled. Without the fix, + // an assert-enabled build can crash when Ledger::read() receives + // a zero-key PermissionedDomain keylet. + if (features[fixCleanup3_2_0]) + { + Env env(*this, features); + auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + PermissionedDEX(env); + + env(pay(bob_, alice_, USD(10)), + Path(~USD), + Sendmax(XRP(10)), + Domain(uint256{}), + Ter(temMALFORMED)); + env.close(); + } + // preclaim - cannot send payment with non existent domain { Env env(*this, features); @@ -1772,7 +1804,9 @@ public: // Test domain offer (w/o hybrid) testOfferCreate(all); + testOfferCreate(all - fixCleanup3_2_0); testPayment(all); + testPayment(all - fixCleanup3_2_0); testBookStep(all); testRippling(all); testOfferTokenIssuerInDomain(all); From 1599c1a672587a6797e6cd125bd8d319737d4418 Mon Sep 17 00:00:00 2001 From: Bart Date: Sat, 30 May 2026 14:48:59 -0400 Subject: [PATCH 22/28] refactor: Revert "perf: Remove unnecessary caches (#5439)" (#7359) Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com> --- cfg/xrpld-example.cfg | 15 +++ include/xrpl/nodestore/Database.h | 4 + .../xrpl/nodestore/detail/DatabaseNodeImp.h | 32 +++++++ .../nodestore/detail/DatabaseRotatingImp.h | 3 + src/libxrpl/nodestore/DatabaseNodeImp.cpp | 95 ++++++++++++++----- src/libxrpl/nodestore/DatabaseRotatingImp.cpp | 6 ++ src/test/app/SHAMapStore_test.cpp | 20 +++- src/xrpld/app/main/Application.cpp | 4 + src/xrpld/app/misc/SHAMapStoreImp.cpp | 26 ++++- src/xrpld/app/misc/SHAMapStoreImp.h | 4 + 10 files changed, 181 insertions(+), 28 deletions(-) diff --git a/cfg/xrpld-example.cfg b/cfg/xrpld-example.cfg index effc62c274..9e334e6f4f 100644 --- a/cfg/xrpld-example.cfg +++ b/cfg/xrpld-example.cfg @@ -953,6 +953,21 @@ # # Optional keys for NuDB and RocksDB: # +# cache_size Size of cache for database records. Default is 16384. +# Setting this value to 0 will use the default value. +# +# cache_age Length of time in minutes to keep database records +# cached. Default is 5 minutes. Setting this value to +# 0 will use the default value. +# +# Note: if cache_size or cache_age is not specified, +# default values will be used for the unspecified +# parameter. +# +# Note: the cache will not be created if online_delete +# is specified, because the rotating NodeStore does +# not use this cache). +# # fast_load Boolean. If set, load the last persisted ledger # from disk upon process start before syncing to # the network. This is likely to improve performance diff --git a/include/xrpl/nodestore/Database.h b/include/xrpl/nodestore/Database.h index ca2dde560c..438a3cc7fc 100644 --- a/include/xrpl/nodestore/Database.h +++ b/include/xrpl/nodestore/Database.h @@ -131,6 +131,10 @@ public: std::uint32_t ledgerSeq, std::function const&)>&& callback); + /** Remove expired entries from the positive and negative caches. */ + virtual void + sweep() = 0; + /** Gather statistics pertaining to read and write activities. * * @param obj Json object reference into which to place counters. diff --git a/include/xrpl/nodestore/detail/DatabaseNodeImp.h b/include/xrpl/nodestore/detail/DatabaseNodeImp.h index dd94b27075..951c60c8c7 100644 --- a/include/xrpl/nodestore/detail/DatabaseNodeImp.h +++ b/include/xrpl/nodestore/detail/DatabaseNodeImp.h @@ -22,6 +22,32 @@ public: beast::Journal j) : Database(scheduler, readThreads, config, j), backend_(std::move(backend)) { + std::optional cacheSize, cacheAge; + + if (config.exists("cache_size")) + { + cacheSize = get(config, "cache_size"); + if (cacheSize.value() < 0) + Throw("Specified negative value for cache_size"); + } + + if (config.exists("cache_age")) + { + cacheAge = get(config, "cache_age"); + if (cacheAge.value() < 0) + Throw("Specified negative value for cache_age"); + } + + if (cacheSize.has_value() || cacheAge.has_value()) + { + cache_ = std::make_shared>( + "DatabaseNodeImp", + cacheSize.value_or(0), + std::chrono::minutes(cacheAge.value_or(0)), + stopwatch(), + j); + } + XRPL_ASSERT( backend_, "xrpl::NodeStore::DatabaseNodeImp::DatabaseNodeImp : non-null " @@ -73,7 +99,13 @@ public: std::uint32_t ledgerSeq, std::function const&)>&& callback) override; + void + sweep() override; + private: + // Cache for database objects. This cache is not always initialized. Check + // for null before using. + std::shared_ptr> cache_; // Persistent key/value storage std::shared_ptr backend_; diff --git a/include/xrpl/nodestore/detail/DatabaseRotatingImp.h b/include/xrpl/nodestore/detail/DatabaseRotatingImp.h index 39441ef4d8..1ba9435a5f 100644 --- a/include/xrpl/nodestore/detail/DatabaseRotatingImp.h +++ b/include/xrpl/nodestore/detail/DatabaseRotatingImp.h @@ -55,6 +55,9 @@ public: void sync() override; + void + sweep() override; + private: std::shared_ptr writableBackend_; std::shared_ptr archiveBackend_; diff --git a/src/libxrpl/nodestore/DatabaseNodeImp.cpp b/src/libxrpl/nodestore/DatabaseNodeImp.cpp index a51c34079b..9323d69131 100644 --- a/src/libxrpl/nodestore/DatabaseNodeImp.cpp +++ b/src/libxrpl/nodestore/DatabaseNodeImp.cpp @@ -24,6 +24,13 @@ DatabaseNodeImp::store(NodeObjectType type, Blob&& data, uint256 const& hash, st auto obj = NodeObject::createObject(type, std::move(data), hash); backend_->store(obj); + if (cache_) + { + // After the store, replace a negative cache entry if there is one + cache_->canonicalize(hash, obj, [](std::shared_ptr const& n) { + return n->getType() == NodeObjectType::Dummy; + }); + } } void @@ -32,9 +39,25 @@ DatabaseNodeImp::asyncFetch( std::uint32_t ledgerSeq, std::function const&)>&& callback) { + if (cache_) + { + std::shared_ptr const obj = cache_->fetch(hash); + if (obj) + { + callback(obj->getType() == NodeObjectType::Dummy ? nullptr : obj); + return; + } + } Database::asyncFetch(hash, ledgerSeq, std::move(callback)); } +void +DatabaseNodeImp::sweep() +{ + if (cache_) + cache_->sweep(); +} + std::shared_ptr DatabaseNodeImp::fetchNodeObject( uint256 const& hash, @@ -42,32 +65,58 @@ DatabaseNodeImp::fetchNodeObject( FetchReport& fetchReport, bool duplicate) { - std::shared_ptr nodeObject = nullptr; - Status status = Status::Ok; + std::shared_ptr nodeObject = cache_ ? cache_->fetch(hash) : nullptr; + if (!nodeObject) + { + JLOG(j_.trace()) << "fetchNodeObject " << hash << ": record not " + << (cache_ ? "cached" : "found"); - try - { - status = backend_->fetch(hash, &nodeObject); - } - catch (std::exception const& e) - { - JLOG(j_.fatal()) << "fetchNodeObject " << hash - << ": Exception fetching from backend: " << e.what(); - rethrow(); - } + Status status = Status::Ok; + try + { + status = backend_->fetch(hash, &nodeObject); + } + catch (std::exception const& e) + { + JLOG(j_.fatal()) << "fetchNodeObject " << hash + << ": Exception fetching from backend: " << e.what(); + rethrow(); + } - switch (status) + switch (status) + { + case Status::Ok: + if (cache_) + { + if (nodeObject) + { + cache_->canonicalizeReplaceClient(hash, nodeObject); + } + else + { + auto notFound = NodeObject::createObject(NodeObjectType::Dummy, {}, hash); + cache_->canonicalizeReplaceClient(hash, notFound); + if (notFound->getType() != NodeObjectType::Dummy) + nodeObject = notFound; + } + } + break; + case Status::NotFound: + break; + case Status::DataCorrupt: + JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": nodestore data is corrupted"; + break; + default: + JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result " + << static_cast(status); + break; + } + } + else { - case Status::Ok: - case Status::NotFound: - break; - case Status::DataCorrupt: - JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": nodestore data is corrupted"; - break; - default: - JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result " - << static_cast(status); - break; + JLOG(j_.trace()) << "fetchNodeObject " << hash << ": record found in cache"; + if (nodeObject->getType() == NodeObjectType::Dummy) + nodeObject.reset(); } if (nodeObject) diff --git a/src/libxrpl/nodestore/DatabaseRotatingImp.cpp b/src/libxrpl/nodestore/DatabaseRotatingImp.cpp index 24b0e2de2e..1de6a83b4c 100644 --- a/src/libxrpl/nodestore/DatabaseRotatingImp.cpp +++ b/src/libxrpl/nodestore/DatabaseRotatingImp.cpp @@ -113,6 +113,12 @@ DatabaseRotatingImp::store(NodeObjectType type, Blob&& data, uint256 const& hash storeStats(1, nObj->getData().size()); } +void +DatabaseRotatingImp::sweep() +{ + // Nothing to do. +} + std::shared_ptr DatabaseRotatingImp::fetchNodeObject( uint256 const& hash, diff --git a/src/test/app/SHAMapStore_test.cpp b/src/test/app/SHAMapStore_test.cpp index fc5ef02465..6e279eadb2 100644 --- a/src/test/app/SHAMapStore_test.cpp +++ b/src/test/app/SHAMapStore_test.cpp @@ -520,6 +520,25 @@ public: ///////////////////////////////////////////////////////////// // Create NodeStore with two backends to allow online deletion of data. // Normally, SHAMapStoreImp handles all these details. + auto nscfg = env.app().config().section(ConfigSection::nodeDatabase()); + + // Provide default values. + if (!nscfg.exists("cache_size")) + { + nscfg.set( + "cache_size", + std::to_string( + env.app().config().getValueFor(SizedItem::TreeCacheSize, std::nullopt))); + } + + if (!nscfg.exists("cache_age")) + { + nscfg.set( + "cache_age", + std::to_string( + env.app().config().getValueFor(SizedItem::TreeCacheAge, std::nullopt))); + } + NodeStoreScheduler scheduler(env.app().getJobQueue()); std::string const writableDb = "write"; @@ -528,7 +547,6 @@ public: auto archiveBackend = makeBackendRotating(env, scheduler, archiveDb); static constexpr int kReadThreads = 4; - auto nscfg = env.app().config().section(ConfigSection::nodeDatabase()); auto dbr = std::make_unique( scheduler, kReadThreads, diff --git a/src/xrpld/app/main/Application.cpp b/src/xrpld/app/main/Application.cpp index a18462f0f7..508dfc8590 100644 --- a/src/xrpld/app/main/Application.cpp +++ b/src/xrpld/app/main/Application.cpp @@ -998,6 +998,10 @@ public: JLOG(journal_.debug()) << "MasterTransaction sweep. Size before: " << oldMasterTxSize << "; size after: " << masterTxCache.size(); } + { + // Sweep NodeStore database cache(s), if enabled. + getNodeStore().sweep(); + } { std::size_t const oldLedgerMasterCacheSize = getLedgerMaster().getFetchPackCacheSize(); diff --git a/src/xrpld/app/misc/SHAMapStoreImp.cpp b/src/xrpld/app/misc/SHAMapStoreImp.cpp index fc3f71c0cc..3f4bd6280f 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.cpp +++ b/src/xrpld/app/misc/SHAMapStoreImp.cpp @@ -165,6 +165,22 @@ std::unique_ptr SHAMapStoreImp::makeNodeStore(int readThreads) { auto nscfg = app_.config().section(ConfigSection::nodeDatabase()); + + // Provide default values. + if (!nscfg.exists("cache_size")) + { + nscfg.set( + "cache_size", + std::to_string(app_.config().getValueFor(SizedItem::TreeCacheSize, std::nullopt))); + } + + if (!nscfg.exists("cache_age")) + { + nscfg.set( + "cache_age", + std::to_string(app_.config().getValueFor(SizedItem::TreeCacheAge, std::nullopt))); + } + std::unique_ptr db; if (deleteInterval_ != 0u) @@ -254,6 +270,8 @@ SHAMapStoreImp::run() LedgerIndex lastRotated = stateDb_.getState().lastRotated; netOPs_ = &app_.getOPs(); ledgerMaster_ = &app_.getLedgerMaster(); + fullBelowCache_ = &(*app_.getNodeFamily().getFullBelowCache()); + treeNodeCache_ = &(*app_.getNodeFamily().getTreeNodeCache()); if (advisoryDelete_) canDelete_ = stateDb_.getCanDelete(); @@ -542,16 +560,16 @@ SHAMapStoreImp::clearCaches(LedgerIndex validatedSeq) // Also clear the FullBelowCache so its generation counter is bumped. // This prevents stale "full below" markers from persisting across // backend rotation/online deletion and interfering with SHAMap sync. - app_.getNodeFamily().getFullBelowCache()->clear(); + fullBelowCache_->clear(); } void SHAMapStoreImp::freshenCaches() { - if (freshenCache(*app_.getNodeFamily().getTreeNodeCache())) + if (freshenCache(*treeNodeCache_)) + return; + if (freshenCache(app_.getMasterTransaction().getCache())) return; - - freshenCache(app_.getMasterTransaction().getCache()); } void diff --git a/src/xrpld/app/misc/SHAMapStoreImp.h b/src/xrpld/app/misc/SHAMapStoreImp.h index 4f0ce04e0d..1803c15e71 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.h +++ b/src/xrpld/app/misc/SHAMapStoreImp.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -93,6 +95,8 @@ private: // as of run() or before NetworkOPs* netOPs_ = nullptr; LedgerMaster* ledgerMaster_ = nullptr; + FullBelowCache* fullBelowCache_ = nullptr; + TreeNodeCache* treeNodeCache_ = nullptr; static constexpr auto kNodeStoreName = "NodeStore"; From 47365f42203c53b723b3d209d8bcd760bf0c4822 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Sat, 30 May 2026 20:23:29 -0400 Subject: [PATCH 23/28] fix: Improve upward rounding edge cases for Number::operator/= (#7328) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com> --- include/xrpl/basics/Number.h | 94 +++++++--- src/libxrpl/basics/Number.cpp | 208 ++++++++++++++++------ src/test/basics/Number_test.cpp | 301 +++++++++++++++++++++++++++++--- 3 files changed, 506 insertions(+), 97 deletions(-) diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index 3aeb4b5bcd..93bef82a8c 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -2,12 +2,14 @@ #include +#include #include #include #include #include #include #include +#include #include #include @@ -40,6 +42,47 @@ isPowerOfTen(T value) return logTen(value).has_value(); } +namespace detail { + +/** Builds a table of the powers of 10 + * + * This function is marked consteval, so it can only be run in + * a constexpr context. This assures that it is and can only be run at + * compile time. Doing it at runtime would be pretty wasteful and + * inefficient. + */ +constexpr std::size_t kInt64Digits = 20; +consteval std::array +buildPowersOfTen() +{ + std::array result{}; + + std::uint64_t power = 1; + std::size_t exponent = 0; + // end the loop early so it doesn't overflow; + for (; exponent < result.size() - 1; ++exponent, power *= 10) + { + result[exponent] = power; + if (power > std::numeric_limits::max() / 10) + throw std::logic_error("Power of 10 table is too big"); + } + result[exponent] = power; + if (power < std::numeric_limits::max() / 10) + throw std::logic_error("Power of 10 table is not big enough for the uint64_t type"); + + return result; +} + +} // namespace detail + +constexpr std::array kPowerOfTen = detail::buildPowersOfTen(); + +static_assert(kPowerOfTen[0] == 1); +static_assert(kPowerOfTen[1] == 10); +static_assert(kPowerOfTen[10] == 10'000'000'000); +static_assert( + isPowerOfTen(kPowerOfTen.back()) && *logTen(kPowerOfTen.back()) == detail::kInt64Digits - 1); + /** MantissaRange defines a range for the mantissa of a normalized Number. * * The mantissa is in the range [min, max], where @@ -76,6 +119,7 @@ isPowerOfTen(T value) struct MantissaRange final { using rep = std::uint64_t; + enum class MantissaScale { Small, // LargeLegacy can be removed when fixCleanup3_2_0 is retired @@ -89,19 +133,15 @@ struct MantissaRange final Enabled = true, }; - explicit constexpr MantissaRange(MantissaScale scale) - : min(getMin(scale)) - , cuspRoundingFixEnabled(isCuspFixEnabled(scale)) - , log(logTen(min).value_or(-1)) - , scale(scale) + explicit constexpr MantissaRange(MantissaScale sc) : scale(sc) { } - rep min; - rep max{(min * 10) - 1}; - CuspRoundingFix cuspRoundingFixEnabled; - int log; - MantissaScale scale; + MantissaScale const scale; + int const log{getExponent(scale)}; + rep const min{getMin(scale, log)}; + rep const max{(min * 10) - 1}; + CuspRoundingFix const cuspRoundingFixEnabled{isCuspFixEnabled(scale)}; static MantissaRange const& getMantissaRange(MantissaScale scale); @@ -110,23 +150,35 @@ struct MantissaRange final getAllScales(); private: - static constexpr rep - getMin(MantissaScale scale) + static constexpr int + getExponent(MantissaScale scale) { switch (scale) { case MantissaScale::Small: - return 1'000'000'000'000'000ULL; + return 15; case MantissaScale::LargeLegacy: case MantissaScale::Large: - return 1'000'000'000'000'000'000ULL; + return 18; + // LCOV_EXCL_START default: // If called in a constexpr context, this throw assures that the build fails if an // invalid scale is used. - throw std::runtime_error("Unknown mantissa scale"); // LCOV_EXCL_LINE + throw std::runtime_error("Unknown mantissa scale"); + // LCOV_EXCL_STOP } } + // Keep this function for future use with different ways to compute + // the ranges. + static constexpr rep + getMin(MantissaScale scale, int exponent) + { + if (exponent < 0 || exponent >= kPowerOfTen.size()) + throw std::runtime_error("Invalid exponent"); // LCOV_EXCL_LINE + return kPowerOfTen[exponent]; + } + static constexpr CuspRoundingFix isCuspFixEnabled(MantissaScale scale) { @@ -477,8 +529,7 @@ public: template < auto MinMantissa, auto MaxMantissa, - Integral64 T = std::decay_t, - Integral64 TMax = std::decay_t> + Integral64 T = std::decay_t> [[nodiscard]] std::pair normalizeToRange() const; @@ -519,7 +570,8 @@ private: int& exponent, MantissaRange::rep const& minMantissa, MantissaRange::rep const& maxMantissa, - MantissaRange::CuspRoundingFix cuspRoundingFixEnabled); + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled, + bool dropped); [[nodiscard]] bool isnormal() const noexcept; @@ -725,16 +777,18 @@ Number::isnormal() const noexcept kMinExponent <= exponent_ && exponent_ <= kMaxExponent); } -template +template std::pair Number::normalizeToRange() const { static_assert(std::is_same_v || std::is_same_v); - static_assert(std::is_same_v); + static_assert(std::is_same_v>); + static_assert(std::is_same_v>); auto constexpr kMIN = static_cast(MinMantissa); auto constexpr kMAX = static_cast(MaxMantissa); static_assert(kMIN > 0); static_assert(kMIN % 10 == 0); + static_assert(isPowerOfTen(kMIN)); static_assert(kMAX % 10 == 9); static_assert((kMAX + 1) / 10 == kMIN); diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index 11f5934b04..275d82d8c9 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -178,6 +178,10 @@ public: setPositive() noexcept; void setNegative() noexcept; + // Should only be called by doNormalize, and then only for division + // operations with remainders. + void + setDropped() noexcept; [[nodiscard]] bool isNegative() const noexcept; @@ -250,6 +254,12 @@ Number::Guard::setNegative() noexcept sbit_ = 1; } +inline void +Number::Guard::setDropped() noexcept +{ + xbit_ = 1; +} + inline bool Number::Guard::isNegative() const noexcept { @@ -398,7 +408,7 @@ Number::Guard::doRoundUp( // _don't_ increment the mantissa. Instead, divide and round recursively. It should // be impossible to recurse more than once, because once the mantissa is divided by // 10, it will be _well_ under maxMantissa and kMaxRep, so adding 1 will have no - // change of bringing it back over. + // chance of bringing it back over. doDropDigit(mantissa, exponent); XRPL_ASSERT_PARTS( safeToIncrement(mantissa), @@ -512,8 +522,6 @@ Number::one() return Number{false, range.min, -range.log, Number::Unchecked{}}; } -// Use the member names in this static function for now so the diff is cleaner -// TODO: Rename the function parameters to get rid of the "_" suffix template void doNormalize( @@ -522,7 +530,8 @@ doNormalize( int& exponent, MantissaRange::rep const& minMantissa, MantissaRange::rep const& maxMantissa, - MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled, + bool dropped) { static constexpr auto kMinExponent = Number::kMinExponent; static constexpr auto kMaxExponent = Number::kMaxExponent; @@ -547,6 +556,8 @@ doNormalize( Guard g; if (negative) g.setNegative(); + if (dropped) + g.setDropped(); while (m > maxMantissa) { if (exponent >= kMaxExponent) @@ -611,7 +622,12 @@ Number::normalize( internalrep const& maxMantissa, MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) { - doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled); + // Not used by every compiler version, and thus not necessarily + // counted by coverage build + // LCOV_EXCL_START + doNormalize( + negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled, false); + // LCOV_EXCL_STOP } template <> @@ -624,7 +640,12 @@ Number::normalize( internalrep const& maxMantissa, MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) { - doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled); + // Not used by every compiler version, and thus not necessarily + // counted by coverage build + // LCOV_EXCL_START + doNormalize( + negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled, false); + // LCOV_EXCL_STOP } template <> @@ -637,7 +658,8 @@ Number::normalize( internalrep const& maxMantissa, MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) { - doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled); + doNormalize( + negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled, false); } void @@ -838,7 +860,9 @@ Number::operator/=(Number const& y) return *this; // n* = numerator // d* = denominator - // *p = negative (positive?) + // z* = result (quotient) + // *p = negative (p for positive, even though the value means not + // positive?) // *s = sign // *m = mantissa // *e = exponent @@ -850,71 +874,155 @@ Number::operator/=(Number const& y) bool const dp = y.negative_; int const ds = (dp ? -1 : 1); - auto dm = y.mantissa_; - auto de = y.exponent_; + // Create the denominator as 128-bit unsigned, since that's what we + // need to work with. + uint128_t const dm = static_cast(y.mantissa_); + auto const de = y.exponent_; auto const& range = kRange.get(); auto const& minMantissa = range.min; auto const& maxMantissa = range.max; auto const cuspRoundingFixEnabled = range.cuspRoundingFixEnabled; - // Shift by 10^17 gives greatest precision while not overflowing - // uint128_t or the cast back to int64_t - // TODO: Can/should this be made bigger for largeRange? - // log(2^128,10) ~ 38.5 - // largeRange.log = 18, fits in 10^19 - // f can be up to 10^(38-19) = 10^19 safely - bool const small = Number::getMantissaScale() == MantissaRange::MantissaScale::Small; - uint128_t const f = small ? 100'000'000'000'000'000 : 10'000'000'000'000'000'000ULL; - XRPL_ASSERT_PARTS(f >= minMantissa * 10, "Number::operator/=", "factor expected size"); + // Division operates on two large integers (16-digit for small + // mantissas, 19-digit for large) using integer math. If the values + // were just divided directly, the result would be only ever be one + // digit or zero - not very useful. + // e.g. 9'876'543'210'987'654 / 1'234'567'890'123'456 = 8 + // 1'234'567'890'123'456 / 9'876'543'210'987'654 = 0 + // Introduce a power-of-ten multiplication factor for the numerator + // which will ensure the result has a meaningful number of digits. + // + // Consider numbers with a 2-digit mantissa: + // * Assume both numbers have an exponent of 0, using "ToNearest" rounding + // * 23 / 67 = 0 + // * Use a factor of 10^4 + // * 230'000 / 67 = 3432 with an exponent of -4 + // * The normalized result will be 34, exponent -2, or 0.34 + // + // The most extreme results are 10/99 and 99/10 + // * 100'000 / 99 = 1'010e-4 = 10e-2 or 0.10 + // * 990'000 / 10 = 99'000e-4 = 99e-1 or 9.9 + // + // Note that the computations give 2 or 3 digits after the + // decimal point to determine which way to round for most scenarios. + // + // For small mantissas (where the MantissaRange.log == 15), shifting by 10^17 gives sufficient + // precision while not overflowing uint128_t or the cast back to int64_t. (This is legacy + // behavior, which must not be changed.) + // + // For large mantissas (where the MantissaRange.log == 18), a shift by 10^20 would be optimal + // for most scenarios. However, larger mantissa values would overflow 2^128. + // + // * log(2^128,10) ~ 38.5 + // * largeRange.log = 18, fits in 10^19 + // * The expanded numerator must fit in 10^38 + // * f not be more than 10^(38-19) = 10^19 safely + // + // So, we do the division into stages: + // + // Stage 1: Use the same factor of 10^17, for the initial division. This + // will frequently not result in a whole number quotient. + // + // Stage 2: If there is a remainder from the first step, repeat the + // process with a "correction" factor of 10^5. Shift the + // result of Stage 1 over by 5 places, and add the second result to it. + // This is equivalent to if we had used an initial factor of 10^22, + // a couple digits more than we actually need. + // + // Stage 3: If there is still a remainder, and the CuspRoundingFix + // is enabled, pass a flag indicating such to doNormalize. The Guard + // in doNormalize will treat that flag as if non-zero digits had + // been dropped from the mantissa when shrinking it into range. + // This is only relevant when rounding away from zero (Upward for + // positive numbers, Downward for negative), or if the "regular" + // remainder is exactly 0.5 for "ToNearest". This will give the + // rounding the most accurate result possible, as if infinite + // precision was used in the initial calculation. - // unsigned denominator - auto const dmu = static_cast(dm); - // correctionFactor can be anything between 10 and f, depending on how much - // extra precision we want to only use for rounding with the - // largeRange. Three digits seems like plenty, and is more than - // the smallRange uses. - uint128_t const correctionFactor = 1'000; + // Stage 1: Do the initial division with a factor of 10^17. + auto constexpr factorExponent = 17; + + uint128_t constexpr f = kPowerOfTen[factorExponent]; auto const numerator = uint128_t(nm) * f; - auto zm = numerator / dmu; - auto ze = ne - de - (small ? 17 : 19); - bool zn = (ns * ds) < 0; - if (!small) + auto zm = numerator / dm; + auto ze = ne - de - factorExponent; + bool zp = (ns * ds) < 0; + // dropped is used in the same way as Guard::xbit_. In the case of + // division, it indicates if there's any remainder left over after + // we have been as precise as reasonable. If there is, it would be as + // if we were using infinite precision math, and a non-zero digit + // had been shifted off the end of the result when normalizing. + bool dropped = false; + + if (range.scale != MantissaRange::MantissaScale::Small) { - // Virtually multiply numerator by correctionFactor. Since that would - // overflow in the existing uint128_t, we'll do that part separately. + // Stage 2 + // + // If there is a remainder, treat it as a secondary numerator. + // Multiply by correctionFactor separately from stage 1. // The math for this would work for small mantissas, but we need to - // preserve existing behavior. + // preserve legacy behavior. // // Consider: - // ((numerator * correctionFactor) / dmu) / correctionFactor - // = ((numerator / dmu) * correctionFactor) / correctionFactor) + // ((numerator * correctionFactor) / dm) / correctionFactor + // = ((numerator / dm) * correctionFactor) / correctionFactor) // // But that assumes infinite precision. With integer math, this is // equivalent to // - // = ((numerator / dmu * correctionFactor) - // + ((numerator % dmu) * correctionFactor) / dmu) / correctionFactor + // = ((numerator / dm * correctionFactor) + // + ((numerator % dm) * correctionFactor) / dm) / correctionFactor + // = ((zm * correctionFactor) + // + (remainder * correctionFactor) / dm) / correctionFactor // - // We have already set `mantissa_ = numerator / dmu`. Now we - // compute `remainder = numerator % dmu`, and if it is - // nonzero, we do the rest of the arithmetic. If it's zero, we can skip - // it. - auto const remainder = (numerator % dmu); + // The trick is that multiplication by correctionFactor is done on the mantissa, but + // division by correctionFactor is done by modifying the exponent, so no precision is lost + // until we normalize. + // + // If remainder is zero, we can skip this stage entirely because + // the first stage gave an exact answer. + auto constexpr correctionExponent = 5; + uint128_t constexpr correctionFactor = kPowerOfTen[correctionExponent]; + static_assert(factorExponent + correctionExponent == 22); + + auto const remainder = (numerator % dm); if (remainder != 0) { - zm *= correctionFactor; - auto const correction = remainder * correctionFactor / dmu; - zm += correction; - // divide by 1000 by moving the exponent, so we don't lose the - // integer value we just computed - ze -= 3; + auto const partialNumerator = remainder * correctionFactor; + auto const correction = partialNumerator / dm; + + // If the correction is zero, we do not have to make any + // modifications to z*, because it will not have any + // effect on the final result. (We'd be adding a bunch of + // zeros to the end of zm that would just be removed in + // normalize.) However, if that is the case, then Stage 3 is + // even more important for accuracy. + if (correction != 0) + { + zm *= correctionFactor; + // divide by the correctionFactor by moving the exponent, so we don't lose the + // integer value we just computed + ze -= correctionExponent; + + zm += correction; + } + + // Stage 3: If there's still anything left, and the cusp + // rounding fix is enabled, flag if there is still + // a remainder from stage 2. + bool const useTrailingRemainder = + cuspRoundingFixEnabled == MantissaRange::CuspRoundingFix::Enabled; + if (useTrailingRemainder) + { + dropped = partialNumerator % dm != 0; + } } } - normalize(zn, zm, ze, minMantissa, maxMantissa, cuspRoundingFixEnabled); - negative_ = zn; + doNormalize(zp, zm, ze, minMantissa, maxMantissa, cuspRoundingFixEnabled, dropped); + negative_ = zp; mantissa_ = static_cast(zm); exponent_ = ze; XRPL_ASSERT_PARTS(isnormal(), "xrpl::Number::operator/=", "result is normalized"); diff --git a/src/test/basics/Number_test.cpp b/src/test/basics/Number_test.cpp index 26060c70e9..81019970ad 100644 --- a/src/test/basics/Number_test.cpp +++ b/src/test/basics/Number_test.cpp @@ -6,11 +6,14 @@ #include #include +// NOLINTNEXTLINE(misc-include-cleaner) +#include #include #include #include #include +#include #include #include #include @@ -40,6 +43,40 @@ class Number_test : public beast::unit_test::Suite return out; } + using dec = boost::multiprecision::cpp_dec_float_50; + + template + static T + pow10(int n) + { + if (n == 0) + return 1; + if (n == 1) + return 10; + + if (n > 1) + { + auto r = pow10(n / 2); + r *= r; + if (n % 2 != 0) + r *= 10; + return r; + } + + // n < 0 + T p = 1; + p /= pow10(-n); + return p; + } + + static std::string + fmt(dec const& v) + { + std::ostringstream os; + os << std::setprecision(40) << v; + return os.str(); + } + public: void testZero() @@ -1589,39 +1626,249 @@ public: void testUpwardRoundsDown() { - testcase << "upward rounding produces a value below exact at kMaxRep cusp"; + auto const scale = Number::getMantissaScale(); + { + testcase << "upward rounding produces a value below exact at kMaxRep cusp " + << to_string(scale); - NumberMantissaScaleGuard const mg{MantissaRange::MantissaScale::Large}; - NumberRoundModeGuard const rg{Number::RoundingMode::Upward}; + NumberRoundModeGuard const rg{Number::RoundingMode::Upward}; - constexpr std::int64_t kAValue = 1'000'000'000'000'049'863LL; - constexpr std::int64_t kBValue = 9'223'372'036'854'315'903LL; + constexpr std::int64_t kAValue = 1'000'000'000'000'049'863LL; + constexpr std::int64_t kBValue = 9'223'372'036'854'315'903LL; - Number const a = kAValue; - Number const b = kBValue; - Number const product = a * b; + Number const a = kAValue; + Number const b = kBValue; + Number const product = a * b; - // Exact reference in BigInt. - BigInt const exactProduct = BigInt(kAValue) * BigInt(kBValue); + // Exact reference in BigInt. + BigInt const exactProduct = BigInt(kAValue) * BigInt(kBValue); - // What Number actually stored. - BigInt storedValue = BigInt(product.mantissa()); - for (int i = 0; i < product.exponent(); ++i) - storedValue *= 10; + // What Number actually stored. + BigInt storedValue = BigInt(product.mantissa()); + for (int i = 0; i < product.exponent(); ++i) + storedValue *= 10; - BigInt const signedDifference = storedValue - exactProduct; + BigInt const signedDifference = storedValue - exactProduct; - log << "\n" - << " a = " << fmt(BigInt(kAValue)) << "\n" - << " b = " << fmt(BigInt(kBValue)) << "\n" - << " exact a*b = " << fmt(exactProduct) << "\n" - << " stored = " << fmt(storedValue) << "\n" - << " stored - exact = " << fmt(signedDifference) << "\n" - << " upward = " << (signedDifference >= 0 ? "held" : "VIOLATED") << "\n"; + log << "\n" + << " a = " << fmt(BigInt(kAValue)) << "\n" + << " b = " << fmt(BigInt(kBValue)) << "\n" + << " exact a*b = " << fmt(exactProduct) << "\n" + << " stored = " << fmt(storedValue) << "\n" + << " stored - exact = " << fmt(signedDifference) << "\n" + << " upward = " << (signedDifference >= 0 ? "held" : "VIOLATED") << "\n" + << " stored.mantissa = " << product.mantissa() << "\n" + << " stored.exponent = " << product.exponent() << "\n"; + log.flush(); - BEAST_EXPECT(signedDifference >= 0); - BEAST_EXPECT(product.mantissa() == (std::numeric_limits::max() / 10) + 1); - BEAST_EXPECT(product.exponent() == 19); + switch (scale) + { + case MantissaRange::MantissaScale::Large: + BEAST_EXPECT(signedDifference >= 0); + BEAST_EXPECT(signedDifference < pow10(product.exponent())); + BEAST_EXPECT( + product.mantissa() == (std::numeric_limits::max() / 10) + 1); + BEAST_EXPECT(product.exponent() == 19); + break; + + case MantissaRange::MantissaScale::LargeLegacy: + BEAST_EXPECT(signedDifference < 0); + BEAST_EXPECT( + product.mantissa() == + (std::numeric_limits::max() / 100) * 100); + BEAST_EXPECT(product.exponent() == 18); + break; + + case MantissaRange::MantissaScale::Small: + // The seemingly weird rounding here is because + // a & b are both normalized, and both round up when + // being converted to Number, so you're really + // getting 1_000_000_000_000_050 * 9_223_372_036_854_316 + BEAST_EXPECT(signedDifference >= 0); + BEAST_EXPECT( + product.mantissa() == + (std::numeric_limits::max() / 1000) + 3); + BEAST_EXPECT(product.exponent() == 21); + break; + } + } + + { + /* Companion regression for the kMaxRep cusp behavior, but for + * `operator/=` on the cusp-fix-ENABLED `Large` scale. + * + * Before the dropped-remainder fix, `operator/=` with Upward + * rounding could return a value STRICTLY LESS than the exact quotient, + * violating Upward's directional invariant. + * + * Mechanism (fix-enabled path): + * 1. `operator/=` computes `numerator = nm * 10^17` and + * `zm = numerator / dm` (integer division, truncates remainder). + * 2. If `remainder != 0`, the correction block runs: + * zm *= 100000 + * correction = (remainder * 100000) / dm // also truncates + * zm += correction + * ze -= 5 + * The truncation in `correction` discards a sub-1/100000 residual. + * 3. `normalize`'s shift loop reduces zm to fit, but the discarded + * residual is BELOW the Guard's visibility, so the Guard sees fraction = 0. + * 4. Under Upward + positive, `round()` returns -1 (no round-up), and + * the algorithm returns the truncated zm + */ + testcase << "operator/= Upward on Large returns value < truth " << to_string(scale); + + NumberRoundModeGuard const roundGuard{Number::RoundingMode::Upward}; + + constexpr std::int64_t aValue = 2LL; + constexpr std::int64_t bValue = 1'000'000'000'000'000'007LL; + // bValue = 10^18 + 7 (prime, in [minMantissa, kMaxRep]). + + Number const a{aValue, 0}; + Number const b{bValue, 0}; + Number const quotient = a / b; + + dec const exact = dec(aValue) / dec(bValue); + dec const stored = dec(quotient.mantissa()) * pow10(quotient.exponent()); + dec const diff = stored - exact; + + log << "\n" + << " a = " << aValue << "\n" + << " b = " << bValue << "\n" + << " exact a/b = " << fmt(exact) << "\n" + << " stored a/b = " << fmt(stored) << "\n" + << " stored - exact = " << fmt(diff) + << " (negative => Upward gave value BELOW truth)\n" + << " quotient.mantissa = " << quotient.mantissa() << "\n" + << " quotient.exponent = " << quotient.exponent() << "\n"; + log.flush(); + + // Upward invariant: stored >= exact. Bug: stored < exact. + switch (scale) + { + case MantissaRange::MantissaScale::Large: + BEAST_EXPECT(stored >= exact); + BEAST_EXPECT(diff < pow10(quotient.exponent())); + break; + + case MantissaRange::MantissaScale::LargeLegacy: + BEAST_EXPECT(stored < exact); + BEAST_EXPECT(diff >= -pow10(quotient.exponent())); + break; + + case MantissaRange::MantissaScale::Small: + // Small mantissa doesn't have the correction for + // dropped remainders + BEAST_EXPECT(stored < exact); + break; + } + } + { + /* Companion test case for Upward positive operator/=: Downward negative + */ + testcase << "operator/= Downward on Large returns value < truth " << to_string(scale); + + NumberRoundModeGuard const roundGuard{Number::RoundingMode::Downward}; + + constexpr std::int64_t aValue = -2LL; + constexpr std::int64_t bValue = 1'000'000'000'000'000'007LL; + // bValue = 10^18 + 7 (prime, in [minMantissa, kMaxRep]). + + Number const a{aValue, 0}; + Number const b{bValue, 0}; + Number const quotient = a / b; + + dec const exact = dec(aValue) / dec(bValue); + dec const stored = dec(quotient.mantissa()) * pow10(quotient.exponent()); + dec const diff = stored - exact; + + log << "\n" + << " a = " << aValue << "\n" + << " b = " << bValue << "\n" + << " exact a/b = " << fmt(exact) << "\n" + << " stored a/b = " << fmt(stored) << "\n" + << " stored - exact = " << fmt(diff) + << " (positive => Downward gave value ABOVE truth)\n" + << " quotient.mantissa = " << quotient.mantissa() << "\n" + << " quotient.exponent = " << quotient.exponent() << "\n"; + log.flush(); + + // invariant: stored <= exact. Bug: stored > exact. + switch (scale) + { + case MantissaRange::MantissaScale::Large: + BEAST_EXPECT(stored <= exact); + BEAST_EXPECT(diff > -pow10(quotient.exponent())); + break; + + case MantissaRange::MantissaScale::LargeLegacy: + BEAST_EXPECT(stored > exact); + BEAST_EXPECT(diff <= pow10(quotient.exponent())); + break; + + case MantissaRange::MantissaScale::Small: + // Small mantissa doesn't have the correction for + // dropped remainders + BEAST_EXPECT(stored < exact); + break; + } + } + { + /* Companion test case for Upward positive operator/=: ToNearest + * + * With ToNearest, if the dropped digits are exactly "5", then the mantissa will be + * rounded to even. The numbers below result in a value where the unrounded mantissa + * ends in an even digit, and "infinite precision" would drop + * "500000000000000000145...", but doNormalize only sees "5". Without the rounding fix, + * doNormalize rounds down to the even value. With the rounding fix, doNormalize knows + * there are more digits beyond "5", and so rounds _up_ to the odd value. + */ + testcase << "operator/= ToNearest on Large returns value < truth " << to_string(scale); + + NumberRoundModeGuard const roundGuard{Number::RoundingMode::ToNearest}; + + constexpr std::int64_t aValue = 1'269'917'268'816'087'809LL; + constexpr std::int64_t bValue = 3'458'525'013'821'685'511LL; + // bValue = 10^18 + 7 (prime, in [minMantissa, kMaxRep]). + + Number const a{aValue, 0}; + Number const b{bValue, 0}; + Number const quotient = a / b; + + dec const exact = dec(aValue) / dec(bValue); + dec const stored = dec(quotient.mantissa()) * pow10(quotient.exponent()); + dec const diff = stored - exact; + + log << "\n" + << " a = " << aValue << "\n" + << " b = " << bValue << "\n" + << " exact a/b = " << fmt(exact) << "\n" + << " stored a/b = " << fmt(stored) << "\n" + << " stored - exact = " << fmt(diff) + << " (negative => ToNearest gave value BELOW truth)\n" + << " quotient.mantissa = " << quotient.mantissa() << "\n" + << " quotient.exponent = " << quotient.exponent() << "\n"; + log.flush(); + + // invariant: stored >= exact. Bug: stored < exact. + switch (scale) + { + case MantissaRange::MantissaScale::Large: + BEAST_EXPECT(stored >= exact); + BEAST_EXPECT(diff < pow10(quotient.exponent())); + break; + + case MantissaRange::MantissaScale::LargeLegacy: + BEAST_EXPECT(stored < exact); + BEAST_EXPECT(diff >= -pow10(quotient.exponent())); + break; + + case MantissaRange::MantissaScale::Small: + // Small mantissa doesn't have the correction for + // dropped remainders + BEAST_EXPECT(stored < exact); + break; + } + } } void @@ -1651,9 +1898,9 @@ public: testTruncate(); testRounding(); testInt64(); + + testUpwardRoundsDown(); } - // This test sets its own number range - testUpwardRoundsDown(); } }; From 99431d78335919fce5910d01966ed8f55b98572a Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Mon, 1 Jun 2026 00:54:23 +0200 Subject: [PATCH 24/28] fix: Pin overpayment principal reduction to exact on-grid value (#7360) --- include/xrpl/ledger/helpers/LendingHelpers.h | 1 + src/libxrpl/ledger/helpers/LendingHelpers.cpp | 138 ++++--- src/test/app/LendingHelpers_test.cpp | 182 +++++---- src/test/app/Loan_test.cpp | 362 ++++++++++++++++++ 4 files changed, 552 insertions(+), 131 deletions(-) diff --git a/include/xrpl/ledger/helpers/LendingHelpers.h b/include/xrpl/ledger/helpers/LendingHelpers.h index b7a1ed6bf2..32f94ee277 100644 --- a/include/xrpl/ledger/helpers/LendingHelpers.h +++ b/include/xrpl/ledger/helpers/LendingHelpers.h @@ -461,6 +461,7 @@ loanAccruedInterest( ExtendedPaymentComponents computeOverpaymentComponents( + Rules const& rules, Asset const& asset, int32_t const loanScale, Number const& overpayment, diff --git a/src/libxrpl/ledger/helpers/LendingHelpers.cpp b/src/libxrpl/ledger/helpers/LendingHelpers.cpp index 9cda5905c9..15ebf33e46 100644 --- a/src/libxrpl/ledger/helpers/LendingHelpers.cpp +++ b/src/libxrpl/ledger/helpers/LendingHelpers.cpp @@ -559,15 +559,34 @@ tryOverpayment( << ", new total value: " << newLoanProperties.loanState.valueOutstanding << ", first payment principal: " << newLoanProperties.firstPaymentPrincipal; - // Calculate what the new loan state should be with the new periodic payment - // including rounding errors - auto const newTheoreticalState = computeTheoreticalLoanState( - rules, - newLoanProperties.periodicPayment, - periodicRate, - paymentRemaining, - managementFeeRate) + - errors; + // Calculate what the new loan state should be with the new periodic payment, + // including the preserved rounding errors. + + auto const newTheoreticalState = [&]() { + auto const state = computeTheoreticalLoanState( + rules, + newLoanProperties.periodicPayment, + periodicRate, + paymentRemaining, + managementFeeRate) + + errors; + + if (!rules.enabled(fixCleanup3_2_0)) + return state; + + // The new principal is known exactly: it is reduced by the overpayment's + // principal portion. computeTheoreticalLoanState instead derives the + // principal -- and, from it, the management fee and interest -- via a + // lossy (P * factor) / factor round-trip. Pin the principal to the exact + // value and re-derive the management fee from the exact interest gross + // (value - principal), so the intermediate state is fully consistent with + // the exact principal rather than the one-scale-unit-high round-trip. + Number const principal = + roundedOldState.principalOutstanding - overpaymentComponents.trackedPrincipalDelta; + Number const managementFee = + tenthBipsOfValue(state.valueOutstanding - principal, managementFeeRate); + return constructLoanState(state.valueOutstanding, principal, managementFee); + }(); JLOG(j.debug()) << "new theoretical value: " << newTheoreticalState.valueOutstanding << ", principal: " << newTheoreticalState.principalOutstanding @@ -762,13 +781,6 @@ doOverpayment( // The proxies still hold the original (pre-overpayment) values, which // allows us to compute deltas and verify they match what we expect // from the overpaymentComponents and loanPaymentParts. - - XRPL_ASSERT_PARTS( - overpaymentComponents.trackedPrincipalDelta == - principalOutstandingProxy - newRoundedLoanState.principalOutstanding, - "xrpl::detail::doOverpayment", - "principal change agrees"); - JLOG(j.debug()) << "valueChange: " << loanPaymentParts.valueChange << ", totalValue before: " << *totalValueOutstandingProxy << ", totalValue after: " << newRoundedLoanState.valueOutstanding @@ -780,35 +792,50 @@ doOverpayment( << overpaymentComponents.trackedPrincipalDelta - (totalValueOutstandingProxy - newRoundedLoanState.valueOutstanding); - // The valueChange returned by tryOverpayment satisfies - // valueChange = (newInterestDue - oldInterestDue) + untrackedInterest. - // Using the loan-state identity v = p + i + m and the adjacent - // `principal change agrees` assertion (dp = oldP - newP), this - // rearranges into three independently-computable terms: - // - // 1. TVO change beyond what principal repayment alone explains: - // newTVO - (oldTVO - dp) - // 2. Management fee released by re-amortization (positive when - // mfee decreased; zero when managementFeeRate == 0): - // oldMfee - newMfee - // 3. The overpayment's penalty interest part (= untrackedInterest - // for the overpayment path; see computeOverpaymentComponents): - // trackedInterestPart() - [[maybe_unused]] Number const tvoChange = newRoundedLoanState.valueOutstanding - - (totalValueOutstandingProxy - overpaymentComponents.trackedPrincipalDelta); - [[maybe_unused]] Number const managementFeeReleased = - managementFeeOutstandingProxy - newRoundedLoanState.managementFeeDue; - [[maybe_unused]] Number const interestPart = overpaymentComponents.trackedInterestPart(); + // The three assertions below are invariants that only hold once + // fixCleanup3_2_0 pins the new principal to the exact reduction + // (oldPrincipal - trackedPrincipalDelta). Before the amendment, the lossy + // (P * factor) / factor round-trip can leave the new principal one + // scale-unit high, so these equalities do not hold on the pre-amendment + // code path and must be gated to match the fix they verify. + if (rules.enabled(fixCleanup3_2_0)) + { + // The valueChange returned by tryOverpayment satisfies + // valueChange = (newInterestDue - oldInterestDue) + untrackedInterest. + // Using the loan-state identity v = p + i + m and the adjacent + // `principal change agrees` assertion (dp = oldP - newP), this + // rearranges into three independently-computable terms: + // + // 1. TVO change beyond what principal repayment alone explains: + // newTVO - (oldTVO - dp) + // 2. Management fee released by re-amortization (positive when + // mfee decreased; zero when managementFeeRate == 0): + // oldMfee - newMfee + // 3. The overpayment's penalty interest part (= untrackedInterest + // for the overpayment path; see computeOverpaymentComponents): + // trackedInterestPart() + [[maybe_unused]] Number const tvoChange = newRoundedLoanState.valueOutstanding - + (totalValueOutstandingProxy - overpaymentComponents.trackedPrincipalDelta); + [[maybe_unused]] Number const managementFeeReleased = + managementFeeOutstandingProxy - newRoundedLoanState.managementFeeDue; + [[maybe_unused]] Number const interestPart = overpaymentComponents.trackedInterestPart(); - XRPL_ASSERT_PARTS( - loanPaymentParts.valueChange == tvoChange + managementFeeReleased + interestPart, - "xrpl::detail::doOverpayment", - "interest paid agrees"); + XRPL_ASSERT_PARTS( + overpaymentComponents.trackedPrincipalDelta == + principalOutstandingProxy - newRoundedLoanState.principalOutstanding, + "xrpl::detail::doOverpayment", + "principal change agrees"); - XRPL_ASSERT_PARTS( - overpaymentComponents.trackedPrincipalDelta == loanPaymentParts.principalPaid, - "xrpl::detail::doOverpayment", - "principal payment matches"); + XRPL_ASSERT_PARTS( + loanPaymentParts.valueChange == tvoChange + managementFeeReleased + interestPart, + "xrpl::detail::doOverpayment", + "interest paid agrees"); + + XRPL_ASSERT_PARTS( + overpaymentComponents.trackedPrincipalDelta == loanPaymentParts.principalPaid, + "xrpl::detail::doOverpayment", + "principal payment matches"); + } // All validations passed, so update the proxy objects (which will // modify the actual Loan ledger object) @@ -1144,11 +1171,13 @@ computePaymentComponents( // Cap each component to never exceed what's actually outstanding deltas.principal = std::min(deltas.principal, currentLedgerState.principalOutstanding); - XRPL_ASSERT_PARTS( - deltas.interest <= currentLedgerState.interestDue, - "xrpl::detail::computePaymentComponents", - "interest due delta not greater than outstanding"); - + if (fixCleanup320Enabled) + { + XRPL_ASSERT_PARTS( + deltas.interest <= currentLedgerState.interestDue, + "xrpl::detail::computePaymentComponents", + "interest due delta not greater than outstanding"); + } // Cap interest to both the outstanding amount AND what's left of the // periodic payment after principal is paid deltas.interest = std::min( @@ -1289,6 +1318,7 @@ computePaymentComponents( */ ExtendedPaymentComponents computeOverpaymentComponents( + Rules const& rules, Asset const& asset, int32_t const loanScale, Number const& overpayment, @@ -1296,10 +1326,13 @@ computeOverpaymentComponents( TenthBips32 const overpaymentFeeRate, TenthBips16 const managementFeeRate) { - XRPL_ASSERT( - overpayment > 0 && isRounded(asset, overpayment, loanScale), - "xrpl::detail::computeOverpaymentComponents : valid overpayment " - "amount"); + if (rules.enabled(fixCleanup3_2_0)) + { + XRPL_ASSERT( + overpayment > 0 && isRounded(asset, overpayment, loanScale), + "xrpl::detail::computeOverpaymentComponents : valid overpayment " + "amount"); + } // First, deduct the fixed overpayment fee from the total amount. // This reduces the effective payment that will be applied to the loan. @@ -2055,6 +2088,7 @@ loanMakePayment( { detail::ExtendedPaymentComponents const overpaymentComponents = detail::computeOverpaymentComponents( + view.rules(), asset, loanScale, overpayment, diff --git a/src/test/app/LendingHelpers_test.cpp b/src/test/app/LendingHelpers_test.cpp index af46dd2e0f..ac8e0764fc 100644 --- a/src/test/app/LendingHelpers_test.cpp +++ b/src/test/app/LendingHelpers_test.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -513,7 +514,9 @@ class LendingHelpers_test : public beast::unit_test::Suite auto const expectedOverpaymentManagementFee = Number{10}; // 10% of 100 auto const expectedPrincipalPortion = Number{400}; // 1,000 - 100 - 500 + Env const env{*this}; auto const components = xrpl::detail::computeOverpaymentComponents( + env.current()->rules(), iou, loanScale, overpayment, @@ -854,7 +857,13 @@ class LendingHelpers_test : public beast::unit_test::Suite Number const overpaymentAmount{50}; auto const overpaymentComponents = computeOverpaymentComponents( - asset, loanScale, overpaymentAmount, TenthBips32(0), TenthBips32(0), managementFeeRate); + env.current()->rules(), + asset, + loanScale, + overpaymentAmount, + TenthBips32(0), + TenthBips32(0), + managementFeeRate); auto const loanProperties = computeLoanProperties( env.current()->rules(), @@ -942,6 +951,7 @@ class LendingHelpers_test : public beast::unit_test::Suite auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval); auto const overpaymentComponents = computeOverpaymentComponents( + env.current()->rules(), asset, loanScale, Number{50, 0}, @@ -1037,6 +1047,7 @@ class LendingHelpers_test : public beast::unit_test::Suite auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval); auto const overpaymentComponents = computeOverpaymentComponents( + env.current()->rules(), asset, loanScale, Number{50, 0}, @@ -1138,6 +1149,7 @@ class LendingHelpers_test : public beast::unit_test::Suite auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval); auto const overpaymentComponents = computeOverpaymentComponents( + env.current()->rules(), asset, loanScale, Number{50, 0}, @@ -1247,6 +1259,7 @@ class LendingHelpers_test : public beast::unit_test::Suite auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval); auto const overpaymentComponents = computeOverpaymentComponents( + env.current()->rules(), asset, loanScale, Number{50, 0}, @@ -1344,7 +1357,6 @@ class LendingHelpers_test : public beast::unit_test::Suite using namespace jtx; using namespace xrpl::detail; - Env const env{*this}; Account const issuer{"issuer"}; PrettyAsset const asset = issuer["USD"]; std::int32_t const loanScale = -5; @@ -1355,7 +1367,9 @@ class LendingHelpers_test : public beast::unit_test::Suite std::uint32_t const paymentsRemaining = 10; auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval); + Env const env{*this}; auto const overpaymentComponents = computeOverpaymentComponents( + env.current()->rules(), asset, loanScale, Number{50, 0}, @@ -1363,87 +1377,97 @@ class LendingHelpers_test : public beast::unit_test::Suite TenthBips32(10'000), // 10% overpayment fee managementFeeRate); - auto const loanProperties = computeLoanProperties( - env.current()->rules(), - asset, - loanPrincipal, - loanInterestRate, - paymentInterval, - paymentsRemaining, - managementFeeRate, - loanScale); + struct Outcome + { + LoanPaymentParts parts; + LoanState oldState; + LoanState newState; + }; - auto const ret = tryOverpayment( - env.current()->rules(), - asset, - loanScale, - overpaymentComponents, - loanProperties.loanState, - loanProperties.periodicPayment, - periodicRate, - paymentsRemaining, - managementFeeRate, - env.journal); + // Run tryOverpayment under a given amendment set. At this (non-near-zero) + // rate computeLoanProperties is amendment-independent, so the loan state + // is identical across the amendment; only tryOverpayment's fixCleanup3_2_0 + // behaviour (the exact-principal pin and the management-fee re-derivation + // from that principal) differs. + auto run = [&](FeatureBitset features) -> std::optional { + Env const env{*this, features}; + auto const loanProperties = computeLoanProperties( + env.current()->rules(), + asset, + loanPrincipal, + loanInterestRate, + paymentInterval, + paymentsRemaining, + managementFeeRate, + loanScale); + auto const ret = tryOverpayment( + env.current()->rules(), + asset, + loanScale, + overpaymentComponents, + loanProperties.loanState, + loanProperties.periodicPayment, + periodicRate, + paymentsRemaining, + managementFeeRate, + env.journal); + if (!BEAST_EXPECT(ret)) + return std::nullopt; + return Outcome{ + .parts = ret->first, + .oldState = loanProperties.loanState, + .newState = ret->second.loanState}; + }; - BEAST_EXPECT(ret); + auto const fixedOpt = run(testableAmendments()); + auto const legacyOpt = run(testableAmendments() - fixCleanup3_2_0); + if (!fixedOpt || !legacyOpt) + { + BEAST_EXPECT(fixedOpt.has_value()); + BEAST_EXPECT(legacyOpt.has_value()); + return; + } + Outcome const& fixed = *fixedOpt; + Outcome const& legacy = *legacyOpt; - auto const& [actualPaymentParts, newLoanProperties] = *ret; - auto const& newState = newLoanProperties.loanState; + // Components that the amendment does not change. The management fee is + // charged against the overpayment interest portion first, so interest + // paid stays 4.5 and fee paid 5.5; the principal repaid is 40 in both. + auto checkCommon = [&](Outcome const& o, char const* tag) { + BEAST_EXPECTS( + (o.parts.interestPaid == Number{45, -1}), + std::string(tag) + " interestPaid " + to_string(o.parts.interestPaid)); + BEAST_EXPECTS( + (o.parts.feePaid == Number{55, -1}), + std::string(tag) + " feePaid " + to_string(o.parts.feePaid)); + BEAST_EXPECTS( + o.parts.principalPaid == 40, + std::string(tag) + " principalPaid " + to_string(o.parts.principalPaid)); + BEAST_EXPECT( + o.parts.principalPaid == + o.oldState.principalOutstanding - o.newState.principalOutstanding); + // v = p + i + m identity: the non-interest part of valueChange equals + // the interest-due change. + BEAST_EXPECT( + o.parts.valueChange - o.parts.interestPaid == + o.newState.interestDue - o.oldState.interestDue); + }; + checkCommon(fixed, "fixed"); + checkCommon(legacy, "legacy"); - // =========== VALIDATE PAYMENT PARTS =========== - - // Since there is loan management fee, the fee is charged against - // overpayment interest portion first, so interest paid remains 4.5 - BEAST_EXPECTS( - (actualPaymentParts.interestPaid == Number{45, -1}), - " interestPaid mismatch: expected 4.5, got " + - to_string(actualPaymentParts.interestPaid)); - - // With overpayment interest portion, value change should equal the - // interest decrease plus overpayment interest portion - BEAST_EXPECTS( - (actualPaymentParts.valueChange == - Number{-164737, -5} + actualPaymentParts.interestPaid), - " valueChange mismatch: expected " + - to_string(Number{-164737, -5} + actualPaymentParts.interestPaid) + ", got " + - to_string(actualPaymentParts.valueChange)); - - // While there is no overpayment fee, fee paid should equal the - // management fee charged against the overpayment interest portion - BEAST_EXPECTS( - (actualPaymentParts.feePaid == Number{55, -1}), - " feePaid mismatch: expected 5.5, got " + to_string(actualPaymentParts.feePaid)); - - BEAST_EXPECTS( - actualPaymentParts.principalPaid == 40, - " principalPaid mismatch: expected 40, got `" + - to_string(actualPaymentParts.principalPaid)); - - // =========== VALIDATE STATE CHANGES =========== - - BEAST_EXPECTS( - actualPaymentParts.principalPaid == - loanProperties.loanState.principalOutstanding - newState.principalOutstanding, - " principalPaid mismatch: expected " + - to_string( - loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + - ", got " + to_string(actualPaymentParts.principalPaid)); - - // Note that the management fee value change is not captured, as this - // value is not needed to correctly update the Vault state. - BEAST_EXPECTS( - (newState.managementFeeDue - loanProperties.loanState.managementFeeDue == - Number{-18304, -5}), - " management fee change mismatch: expected " + to_string(Number{-18304, -5}) + - ", got " + - to_string(newState.managementFeeDue - loanProperties.loanState.managementFeeDue)); - - BEAST_EXPECTS( - actualPaymentParts.valueChange - actualPaymentParts.interestPaid == - newState.interestDue - loanProperties.loanState.interestDue, - " valueChange mismatch: expected " + - to_string(newState.interestDue - loanProperties.loanState.interestDue) + ", got " + - to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid)); + // With fixCleanup3_2_0 the management fee is re-derived from the exact + // principal; without it, from the one-scale-unit-high round-trip + // principal. So the management fee outstanding (and hence the value + // change, via v = p + i + m) differ by exactly one scale-unit (1e-5 at + // loanScale -5) between the two paths. + BEAST_EXPECT((fixed.parts.valueChange == Number{-164738, -5} + fixed.parts.interestPaid)); + BEAST_EXPECT( + (fixed.newState.managementFeeDue - fixed.oldState.managementFeeDue == + Number{-18303, -5})); + BEAST_EXPECT((legacy.parts.valueChange == Number{-164737, -5} + legacy.parts.interestPaid)); + BEAST_EXPECT( + (legacy.newState.managementFeeDue - legacy.oldState.managementFeeDue == + Number{-18304, -5})); } public: diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index c380655563..c3b5850231 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -7580,6 +7580,366 @@ protected: attemptWithdrawShares(depositorB, sharesLpB, tesSUCCESS); } + // A residual overpayment can reduce the stored principal by one scale-unit + // *less* than computeOverpaymentComponents predicts, firing the + // "principal change agrees" XRPL_ASSERT_PARTS in doOverpayment: + // + // trackedPrincipalDelta == principalOutstanding - newPrincipalOutstanding + // + // tryOverpayment re-amortizes the loan at the reduced principal, then + // re-derives the theoretical principal from the new periodic payment via + // (P * paymentFactor) / paymentFactor. That round-trip is not exact in + // Number's 19-digit arithmetic; a positive residual pushes the recomputed + // principal a hair above the exact grid point `oldPrincipal - delta`, and + // the Upward rounding in tryOverpayment then bumps it a full scale-unit + // higher. The principal therefore drops by `delta - 1 unit`, not `delta`. + // + // Concrete case (isolated, at the tryOverpayment level): + // A 100 USD loan at the minimum non-zero rate, 3 payments, loanScale -10. + // After one regular payment (principalOutstanding 66.6666666674) a residual overpayment of + // 0.049999998 yields trackedPrincipalDelta 0.048999998 but only reduces the principal by + // 0.0489999979 (newPrincipal 66.6176666695) — short by 1e-10. + // + // With fixCleanup3_2_0, tryOverpayment pins the new principal to the exact, + // on-grid reduction (oldPrincipal - trackedPrincipalDelta) instead of the + // lossy (P*factor)/factor round-trip, so the assertion holds and the + // overpayment applies cleanly. The three "principal change agrees" / + // "interest paid agrees" / "principal payment matches" assertions are + // gated behind the same amendment, so without it they are disabled (the + // server does not abort) and the loan keeps the pre-amendment computation. + // + // The test runs the same scenario under both amendment settings and checks + // the stored principal against a ground-truth value derived independently of + // the loan-state computation under test. + void + testBugOverpaymentPrincipalChange() + { + testcase("bug: doOverpayment asserts 'principal change agrees'"); + + using namespace jtx; + using namespace loan; + using namespace xrpl::detail; + + struct Params + { + TenthBips32 interestRate; + TenthBips16 managementFeeRate; + std::uint32_t paymentTotal; + std::uint32_t paymentInterval; + std::int64_t principal; + Number overpayment; + TenthBips32 overpaymentInterestRate; + TenthBips32 overpaymentFeeRate; + std::optional vaultScale; + }; + + struct Result + { + Number principalOutstanding; // stored principal after the LoanPay + Number expectedNewPrincipal; // ground truth, independent of the fix + Number managementFeeChange; // managementFeeOutstanding after - before + Number unit; // one scale-unit at the loan scale + }; + + auto runScenario = [this](FeatureBitset features, Params const& p) -> Result { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const lender{"vaultOwner"}; + Account const borrower{"borrower"}; + + env.fund(XRP(1'000'000), issuer, lender, borrower); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const iouAsset = issuer["USD"]; + Asset const asset = iouAsset.raw(); + STAmount const iouLimit{asset, Number{9'999'999'999'999'999LL}}; + env(trust(lender, iouLimit)); + env(trust(borrower, iouLimit)); + env(pay(issuer, lender, iouAsset(1'000'000))); + env(pay(issuer, borrower, iouAsset(1'000'000))); + env.close(); + + auto const broker = createVaultAndBroker( + env, + iouAsset, + lender, + {.vaultDeposit = 900'000, + .debtMax = 0, + .managementFeeRate = p.managementFeeRate, + .vaultScale = p.vaultScale}); + + auto const brokerSle = env.le(broker.brokerKeylet()); + BEAST_EXPECT(brokerSle); + auto const loanSequence = brokerSle ? brokerSle->at(sfLoanSequence) : 0; + auto const loanKeylet = keylet::loan(broker.brokerID, loanSequence); + + env(set(borrower, broker.brokerID, Number{p.principal}, tfLoanOverpayment), + Sig(sfCounterpartySignature, lender), + kInterestRate(p.interestRate), + kPaymentTotal(p.paymentTotal), + kPaymentInterval(p.paymentInterval), + kGracePeriod(p.paymentInterval), + kOverpaymentFee(p.overpaymentFeeRate), + kOverpaymentInterestRate(p.overpaymentInterestRate), + Fee(env.current()->fees().base * 2), + Ter(tesSUCCESS)); + env.close(); + + // The single LoanPay below makes one regular payment (the overpayment + // is smaller than one period) and leaves the residual as an + // overpayment. + auto const s = getCurrentState(env, broker, loanKeylet); + auto const periodicRate = loanPeriodicRate(s.interestRate, s.paymentInterval); + auto const onePeriod = computePaymentComponents( + env.current()->rules(), + asset, + s.loanScale, + s.totalValue, + s.principalOutstanding, + s.managementFeeOutstanding, + s.periodicPayment, + periodicRate, + s.paymentRemaining, + p.managementFeeRate); + + // Ground truth: the stored principal must drop by exactly the regular + // payment's principal portion plus the overpayment's principal + // portion. computeOverpaymentComponents depends only on the + // overpayment amount and rates (not on the loan-state computation + // under test), so it is an independent oracle. Both components are + // computed under the same rules as the env so the payment factor + // matches. + auto const overpaymentComponents = computeOverpaymentComponents( + env.current()->rules(), + asset, + s.loanScale, + p.overpayment, + p.overpaymentInterestRate, + p.overpaymentFeeRate, + p.managementFeeRate); + Number const expectedNewPrincipal = s.principalOutstanding - + onePeriod.trackedPrincipalDelta - overpaymentComponents.trackedPrincipalDelta; + + Number const managementFeeBefore = s.managementFeeOutstanding; + + STAmount const payAmount{asset, onePeriod.trackedValueDelta + p.overpayment}; + env(pay(borrower, loanKeylet.key, payAmount), + Txflags(tfLoanOverpayment), + Ter(tesSUCCESS)); + env.close(); + + auto const loanSle = env.le(loanKeylet); + BEAST_EXPECT(loanSle); + + return Result{ + .principalOutstanding = loanSle ? Number{loanSle->at(sfPrincipalOutstanding)} : 0, + .expectedNewPrincipal = expectedNewPrincipal, + .managementFeeChange = + (loanSle ? Number{loanSle->at(sfManagementFeeOutstanding)} : Number{0}) - + managementFeeBefore, + .unit = Number{1, s.loanScale}}; + }; + + // Scenario 1: the original near-zero-rate principal reproduction + // (loanScale -10, no management fee). 0.049999998 is smaller than one + // period, so it stays a residual overpayment. + Params const principalCase{ + .interestRate = TenthBips32{1}, + .managementFeeRate = TenthBips16{0}, + .paymentTotal = 3, + .paymentInterval = 60, + .principal = 100, + .overpayment = Number{49999998, -9}, + .overpaymentInterestRate = TenthBips32{1000}, + .overpaymentFeeRate = TenthBips32{1000}, + .vaultScale = 1}; + + // With fixCleanup3_2_0 the stored principal lands exactly on the + // ground-truth grid point: it is reduced by exactly the overpayment's + // principal portion. This is the key correctness check: if the principal + // pin were removed (even with the assertions still gated off), the lossy + // (P * factor) / factor round-trip would leave the principal one + // scale-unit high and this would fail. + Result const fixed = runScenario(all_, principalCase); + BEAST_EXPECTS( + fixed.principalOutstanding == fixed.expectedNewPrincipal, + "fixed principal " + to_string(fixed.principalOutstanding) + " != expected " + + to_string(fixed.expectedNewPrincipal)); + + // Without the amendment the loan amortizes with the catastrophically + // cancelling near-zero payment factor, so its schedule (and ground truth) + // differ from the fixed case; the gated assertions keep the server from + // aborting and the overpayment still lands exactly on that schedule. + Result const legacy = runScenario(all_ - fixCleanup3_2_0, principalCase); + BEAST_EXPECTS( + legacy.principalOutstanding == legacy.expectedNewPrincipal, + "legacy principal " + to_string(legacy.principalOutstanding) + " != expected " + + to_string(legacy.expectedNewPrincipal)); + + // Scenario 2: a normal-rate loan with a 10% management fee. At a normal + // rate the payment factor is identical across the amendment, so toggling + // fixCleanup3_2_0 isolates the fix. This overpayment (found by search) + // lands on a state where both the principal and the management fee differ + // by one scale-unit between the fixed and legacy paths. + Params const feeCase{ + .interestRate = TenthBips32{10000}, + .managementFeeRate = TenthBips16{10000}, + .paymentTotal = 6, + .paymentInterval = 30u * 24 * 60 * 60, + .principal = 1000, + .overpayment = Number{214367363, -10}, + .overpaymentInterestRate = TenthBips32{0}, + .overpaymentFeeRate = TenthBips32{0}, + .vaultScale = std::nullopt}; + + Result const feeFixed = runScenario(all_, feeCase); + Result const feeLegacy = runScenario(all_ - fixCleanup3_2_0, feeCase); + + // With the fix the principal is the exact reduction; without it the lossy + // (P * factor) / factor round-trip leaves it one scale-unit high. + BEAST_EXPECTS( + feeFixed.principalOutstanding == feeFixed.expectedNewPrincipal, + "fee-case fixed principal " + to_string(feeFixed.principalOutstanding) + + " != expected " + to_string(feeFixed.expectedNewPrincipal)); + BEAST_EXPECTS( + feeLegacy.principalOutstanding == feeLegacy.expectedNewPrincipal + feeLegacy.unit, + "fee-case legacy principal " + to_string(feeLegacy.principalOutstanding) + + " != expected " + to_string(feeLegacy.expectedNewPrincipal + feeLegacy.unit)); + + // Management fee: the overpayment re-amortizes a fee-bearing loan, so the management fee + // outstanding drops. + // + // Unlike the principal that is already at the correct precision, the re-amortized + // management fee is tenthBipsOfValue of the new schedule's gross interest, which depends + // on the recomputed periodic payment. So the expected change below is a pinned constant + // captured from a passing run a magic value only because there is nothing simpler to + // compare against. + // + // At the integration level, toggling the amendment also changes the regular payment's + // rounding so a fixed-vs-legacy comparison cannot isolate the overpayment management-fee + // fix. + BEAST_EXPECT(feeFixed.managementFeeChange == feeLegacy.managementFeeChange); + BEAST_EXPECTS( + (feeFixed.managementFeeChange == Number{-8219709543, -10}), + "fee-case mgmt fee change " + to_string(feeFixed.managementFeeChange)); + } + + // A LoanSet with InterestRate = 1 (0.001% annualized, the minimum non-zero + // rate). At such a near-zero rate the closed-form payment factor + // (1 + r)^n - 1 cancels catastrophically. + // + // Without fixCleanup3_2_0 the resulting amortization is degenerate and the + // LoanSet is rejected with tecPRECISION_LOSS (no loan created). With the + // amendment, computePowerMinusOneHybrid uses a numerically-stable series + // expansion, so the loan is created and the scheduled payments + // (2 * periodicPayment) cover the principal — no economic underpayment + // (yield theft). + // + // The test runs the same LoanSet under both amendment settings and pins the + // exact outcome for each. + void + testLoanSetNearZeroInterestRateSucceeds() + { + testcase("LoanSet near-zero interest rate covers principal"); + + using namespace jtx; + using namespace loan; + + Number const principalRequested{1000}; + + struct Result + { + TER ter = tesSUCCESS; + bool created = false; + std::int32_t loanScale = 0; + Number principal; + Number totalValue; + Number managementFee; + Number periodicPayment; + }; + + auto runScenario = [&](FeatureBitset features, TER expectedTer) -> Result { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const lender{"vaultOwner"}; + Account const borrower{"borrower"}; + + env.fund(XRP(1'000'000), issuer, lender, borrower); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const iouAsset = issuer["USD"]; + STAmount const iouLimit{iouAsset.raw(), Number{9'999'999'999'999'999LL}}; + env(trust(lender, iouLimit)); + env(trust(borrower, iouLimit)); + env(pay(issuer, lender, iouAsset(1'000'000))); + env(pay(issuer, borrower, iouAsset(1'000'000))); + env.close(); + + auto const broker = createVaultAndBroker( + env, + iouAsset, + lender, + {.vaultDeposit = 100'000, .debtMax = 0, .managementFeeRate = TenthBips16{0}}); + + auto const brokerSle = env.le(broker.brokerKeylet()); + BEAST_EXPECT(brokerSle); + auto const loanSequence = brokerSle ? brokerSle->at(sfLoanSequence) : 0; + auto const loanKeylet = keylet::loan(broker.brokerID, loanSequence); + + env(set(borrower, broker.brokerID, principalRequested), + Sig(sfCounterpartySignature, lender), + kInterestRate(TenthBips32{1}), + kPaymentTotal(2), + kPaymentInterval(400), + Fee(env.current()->fees().base * 2), + Ter(expectedTer)); + env.close(); + + Result r; + r.ter = env.ter(); + if (auto const loanSle = env.le(loanKeylet)) + { + r.created = true; + r.loanScale = loanSle->at(sfLoanScale); + r.principal = loanSle->at(sfPrincipalOutstanding); + r.totalValue = loanSle->at(sfTotalValueOutstanding); + r.managementFee = loanSle->at(sfManagementFeeOutstanding); + r.periodicPayment = loanSle->at(sfPeriodicPayment); + } + return r; + }; + + Result const fixed = runScenario(all_, tesSUCCESS); + Result const legacy = runScenario(all_ - fixCleanup3_2_0, tecPRECISION_LOSS); + + // Without the amendment, the catastrophically-cancelling closed-form + // payment factor produces a degenerate amortization that fails + // checkLoanGuards: the LoanSet is rejected with tecPRECISION_LOSS and no + // loan is created. + BEAST_EXPECT(legacy.ter == tecPRECISION_LOSS); + BEAST_EXPECT(!legacy.created); + + // With the amendment the stable series expansion produces a valid loan + // at loanScale -10. + BEAST_EXPECT(fixed.ter == tesSUCCESS); + BEAST_EXPECT(fixed.created); + BEAST_EXPECT(fixed.loanScale == -10); + BEAST_EXPECT(fixed.principal == principalRequested); + BEAST_EXPECT((fixed.totalValue == Number{10000000001903, -10})); + BEAST_EXPECT(fixed.managementFee == beast::kZero); + + // Periodic payment from the numerically-stable series expansion, and the + // scheduled total (2 * periodicPayment) which exceeds the 1000 principal + // — no economic underpayment / yield theft. + BEAST_EXPECT((fixed.periodicPayment == Number{5000000000951293762, -16})); + BEAST_EXPECT((fixed.periodicPayment * 2 == Number{1000000000190258752, -15})); + BEAST_EXPECT(fixed.periodicPayment * 2 > principalRequested); + } + // An overpayment whose residual amount has more precision than loanScale // fires the isRounded(asset, overpayment, loanScale) assertion in // computeOverpaymentComponents (and a downstream "interest paid agrees" @@ -8358,12 +8718,14 @@ protected: testLimitExceeded(); testLoanSetBlockedLoanPayAllowedWhenCanTransferCleared(); testLendingCanTradeClearedNoImpact(); + testBugOverpaymentPrincipalChange(); testBugOverpayUnroundedAmount(); for (auto const flags : {0u, tfLoanOverpayment}) testYieldTheftRounding(flags); testBugInterestDueDeltaCrash(); testFullLifecycleVaultPnLNearZeroRate(); + testLoanSetNearZeroInterestRateSucceeds(); } // Tests run under each entry in amendmentCombinations(). From 7e15621e7b79686da649f5bffb0b56d7eaa33d66 Mon Sep 17 00:00:00 2001 From: Bart Date: Sun, 31 May 2026 18:55:18 -0400 Subject: [PATCH 25/28] release: Bump version to 3.2.0-rc3 (#7371) Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com> --- src/libxrpl/protocol/BuildInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/protocol/BuildInfo.cpp b/src/libxrpl/protocol/BuildInfo.cpp index 2e33d03088..7de1862dfc 100644 --- a/src/libxrpl/protocol/BuildInfo.cpp +++ b/src/libxrpl/protocol/BuildInfo.cpp @@ -23,7 +23,7 @@ namespace { //------------------------------------------------------------------------------ // clang-format off // NOLINTNEXTLINE(readability-identifier-naming) -char const* const versionString = "3.2.0-rc2" +char const* const versionString = "3.2.0-rc3" // clang-format on ; From 0fffe23abc3a42e7d8016fbbd9a0beed3c40bbc9 Mon Sep 17 00:00:00 2001 From: Michael Legleux Date: Sun, 31 May 2026 20:33:19 -0700 Subject: [PATCH 26/28] fix: Adjust xrpld systemd service and update timer (#7374) --- package/debian/rules | 2 +- package/shared/update-xrpld.timer | 2 +- package/shared/xrpld.service | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package/debian/rules b/package/debian/rules index cd94da7e5b..612fe1b1a9 100644 --- a/package/debian/rules +++ b/package/debian/rules @@ -10,7 +10,7 @@ override_dh_auto_configure override_dh_auto_build override_dh_auto_test: override_dh_installsystemd: dh_installsystemd --no-stop-on-upgrade xrpld.service - dh_installsystemd --name=update-xrpld --no-start update-xrpld.service update-xrpld.timer + dh_installsystemd --name=update-xrpld --no-enable --no-start update-xrpld.service update-xrpld.timer execute_before_dh_installtmpfiles: dh_installsysusers diff --git a/package/shared/update-xrpld.timer b/package/shared/update-xrpld.timer index 21dabf1400..9fba09d30a 100644 --- a/package/shared/update-xrpld.timer +++ b/package/shared/update-xrpld.timer @@ -3,7 +3,7 @@ Description=Daily xrpld update check [Timer] OnCalendar=*-*-* 00:00:00 -RandomizedDelaySec=24h +RandomizedDelaySec=4h Persistent=true [Install] diff --git a/package/shared/xrpld.service b/package/shared/xrpld.service index 72b6cc9938..8e10ed2eee 100644 --- a/package/shared/xrpld.service +++ b/package/shared/xrpld.service @@ -2,14 +2,15 @@ Description=XRP Ledger Daemon After=network-online.target Wants=network-online.target -StartLimitIntervalSec=300 +StartLimitIntervalSec=5min StartLimitBurst=5 [Service] Type=simple ExecStart=/usr/bin/xrpld --net --silent --conf /etc/xrpld/xrpld.cfg -Restart=always +Restart=on-failure RestartSec=5s +TimeoutStopSec=5min NoNewPrivileges=true ProtectSystem=full ProtectHome=true From 109b64910616a84aad9b7a06514526431c8fa489 Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Mon, 1 Jun 2026 17:27:13 +0200 Subject: [PATCH 27/28] refactor: Use `STLedgerEntry` type aliases instead of `std::shared_ptr` (#7282) --- .../codegen/templates/LedgerEntry.h.mako | 4 +- include/xrpl/basics/TaggedCache.h | 2 +- include/xrpl/ledger/ApplyView.h | 20 ++--- include/xrpl/ledger/ApplyViewImpl.h | 4 +- include/xrpl/ledger/BookDirs.h | 6 +- include/xrpl/ledger/CachedView.h | 2 +- include/xrpl/ledger/Dir.h | 6 +- include/xrpl/ledger/Ledger.h | 10 +-- include/xrpl/ledger/OpenView.h | 8 +- include/xrpl/ledger/RawView.h | 6 +- include/xrpl/ledger/ReadView.h | 6 +- include/xrpl/ledger/View.h | 7 +- include/xrpl/ledger/detail/ApplyStateTable.h | 30 +++---- include/xrpl/ledger/detail/ApplyViewBase.h | 16 ++-- include/xrpl/ledger/detail/RawStateTable.h | 12 +-- include/xrpl/ledger/helpers/AMMHelpers.h | 4 +- .../xrpl/ledger/helpers/AccountRootHelpers.h | 13 +-- .../xrpl/ledger/helpers/CredentialHelpers.h | 4 +- include/xrpl/ledger/helpers/DelegateHelpers.h | 4 +- .../xrpl/ledger/helpers/DirectoryHelpers.h | 22 ++--- include/xrpl/ledger/helpers/EscrowHelpers.h | 6 +- include/xrpl/ledger/helpers/NFTokenHelpers.h | 13 +-- include/xrpl/ledger/helpers/OfferHelpers.h | 4 +- .../ledger/helpers/PaymentChannelHelpers.h | 6 +- .../xrpl/ledger/helpers/RippleStateHelpers.h | 6 +- include/xrpl/ledger/helpers/VaultHelpers.h | 24 ++---- .../xrpl/protocol_autogen/LedgerEntryBase.h | 6 +- .../protocol_autogen/ledger_entries/AMM.h | 4 +- .../ledger_entries/AccountRoot.h | 4 +- .../ledger_entries/Amendments.h | 4 +- .../protocol_autogen/ledger_entries/Bridge.h | 4 +- .../protocol_autogen/ledger_entries/Check.h | 4 +- .../ledger_entries/Credential.h | 4 +- .../protocol_autogen/ledger_entries/DID.h | 4 +- .../ledger_entries/Delegate.h | 4 +- .../ledger_entries/DepositPreauth.h | 4 +- .../ledger_entries/DirectoryNode.h | 4 +- .../protocol_autogen/ledger_entries/Escrow.h | 4 +- .../ledger_entries/FeeSettings.h | 4 +- .../ledger_entries/LedgerHashes.h | 4 +- .../protocol_autogen/ledger_entries/Loan.h | 4 +- .../ledger_entries/LoanBroker.h | 4 +- .../protocol_autogen/ledger_entries/MPToken.h | 4 +- .../ledger_entries/MPTokenIssuance.h | 4 +- .../ledger_entries/NFTokenOffer.h | 4 +- .../ledger_entries/NFTokenPage.h | 4 +- .../ledger_entries/NegativeUNL.h | 4 +- .../protocol_autogen/ledger_entries/Offer.h | 4 +- .../protocol_autogen/ledger_entries/Oracle.h | 4 +- .../ledger_entries/PayChannel.h | 4 +- .../ledger_entries/PermissionedDomain.h | 4 +- .../ledger_entries/RippleState.h | 4 +- .../ledger_entries/SignerList.h | 4 +- .../protocol_autogen/ledger_entries/Ticket.h | 4 +- .../protocol_autogen/ledger_entries/Vault.h | 4 +- .../ledger_entries/XChainOwnedClaimID.h | 4 +- .../XChainOwnedCreateAccountClaimID.h | 4 +- include/xrpl/tx/ApplyContext.h | 4 +- include/xrpl/tx/Transactor.h | 7 +- include/xrpl/tx/invariants/AMMInvariant.h | 2 +- include/xrpl/tx/invariants/FreezeInvariant.h | 19 ++-- include/xrpl/tx/invariants/InvariantCheck.h | 35 ++++---- .../xrpl/tx/invariants/LoanBrokerInvariant.h | 2 +- include/xrpl/tx/invariants/LoanInvariant.h | 2 +- include/xrpl/tx/invariants/MPTInvariant.h | 4 +- include/xrpl/tx/invariants/NFTInvariant.h | 4 +- .../tx/invariants/PermissionedDEXInvariant.h | 2 +- .../invariants/PermissionedDomainInvariant.h | 2 +- include/xrpl/tx/invariants/VaultInvariant.h | 2 +- include/xrpl/tx/paths/BookTip.h | 2 +- .../tx/transactors/account/AccountDelete.h | 5 +- .../xrpl/tx/transactors/account/AccountSet.h | 5 +- .../tx/transactors/account/SetRegularKey.h | 5 +- .../tx/transactors/account/SignerListSet.h | 5 +- .../xrpl/tx/transactors/bridge/XChainBridge.h | 40 ++------- .../xrpl/tx/transactors/check/CheckCancel.h | 5 +- include/xrpl/tx/transactors/check/CheckCash.h | 5 +- .../xrpl/tx/transactors/check/CheckCreate.h | 5 +- .../credentials/CredentialAccept.h | 5 +- .../credentials/CredentialCreate.h | 5 +- .../credentials/CredentialDelete.h | 5 +- .../tx/transactors/delegate/DelegateSet.h | 7 +- include/xrpl/tx/transactors/dex/AMMBid.h | 5 +- include/xrpl/tx/transactors/dex/AMMClawback.h | 5 +- include/xrpl/tx/transactors/dex/AMMCreate.h | 5 +- include/xrpl/tx/transactors/dex/AMMDelete.h | 5 +- include/xrpl/tx/transactors/dex/AMMDeposit.h | 5 +- include/xrpl/tx/transactors/dex/AMMVote.h | 5 +- include/xrpl/tx/transactors/dex/AMMWithdraw.h | 7 +- include/xrpl/tx/transactors/dex/OfferCancel.h | 5 +- include/xrpl/tx/transactors/dex/OfferCreate.h | 7 +- include/xrpl/tx/transactors/did/DIDDelete.h | 7 +- include/xrpl/tx/transactors/did/DIDSet.h | 5 +- .../xrpl/tx/transactors/escrow/EscrowCancel.h | 5 +- .../xrpl/tx/transactors/escrow/EscrowCreate.h | 5 +- .../xrpl/tx/transactors/escrow/EscrowFinish.h | 5 +- .../lending/LoanBrokerCoverClawback.h | 5 +- .../lending/LoanBrokerCoverDeposit.h | 5 +- .../lending/LoanBrokerCoverWithdraw.h | 5 +- .../tx/transactors/lending/LoanBrokerDelete.h | 5 +- .../tx/transactors/lending/LoanBrokerSet.h | 5 +- .../xrpl/tx/transactors/lending/LoanDelete.h | 5 +- .../xrpl/tx/transactors/lending/LoanManage.h | 5 +- include/xrpl/tx/transactors/lending/LoanPay.h | 5 +- include/xrpl/tx/transactors/lending/LoanSet.h | 5 +- .../tx/transactors/nft/NFTokenAcceptOffer.h | 9 +- include/xrpl/tx/transactors/nft/NFTokenBurn.h | 5 +- .../tx/transactors/nft/NFTokenCancelOffer.h | 5 +- .../tx/transactors/nft/NFTokenCreateOffer.h | 5 +- include/xrpl/tx/transactors/nft/NFTokenMint.h | 5 +- .../xrpl/tx/transactors/nft/NFTokenModify.h | 5 +- .../xrpl/tx/transactors/oracle/OracleDelete.h | 11 +-- .../xrpl/tx/transactors/oracle/OracleSet.h | 5 +- .../tx/transactors/payment/DepositPreauth.h | 5 +- include/xrpl/tx/transactors/payment/Payment.h | 5 +- .../payment_channel/PaymentChannelClaim.h | 5 +- .../payment_channel/PaymentChannelCreate.h | 5 +- .../payment_channel/PaymentChannelFund.h | 5 +- .../PermissionedDomainDelete.h | 5 +- .../PermissionedDomainSet.h | 5 +- include/xrpl/tx/transactors/system/Batch.h | 5 +- include/xrpl/tx/transactors/system/Change.h | 5 +- .../tx/transactors/system/LedgerStateFix.h | 5 +- .../xrpl/tx/transactors/system/TicketCreate.h | 5 +- include/xrpl/tx/transactors/token/Clawback.h | 5 +- .../tx/transactors/token/MPTokenAuthorize.h | 5 +- .../transactors/token/MPTokenIssuanceCreate.h | 5 +- .../token/MPTokenIssuanceDestroy.h | 5 +- .../tx/transactors/token/MPTokenIssuanceSet.h | 5 +- include/xrpl/tx/transactors/token/TrustSet.h | 5 +- .../xrpl/tx/transactors/vault/VaultClawback.h | 9 +- .../xrpl/tx/transactors/vault/VaultCreate.h | 5 +- .../xrpl/tx/transactors/vault/VaultDelete.h | 5 +- .../xrpl/tx/transactors/vault/VaultDeposit.h | 5 +- include/xrpl/tx/transactors/vault/VaultSet.h | 5 +- .../xrpl/tx/transactors/vault/VaultWithdraw.h | 5 +- src/libxrpl/ledger/ApplyStateTable.cpp | 28 +++--- src/libxrpl/ledger/ApplyView.cpp | 6 +- src/libxrpl/ledger/ApplyViewBase.cpp | 16 ++-- src/libxrpl/ledger/ApplyViewImpl.cpp | 9 +- src/libxrpl/ledger/CachedView.cpp | 3 +- src/libxrpl/ledger/Ledger.cpp | 10 +-- src/libxrpl/ledger/OpenView.cpp | 8 +- src/libxrpl/ledger/RawStateTable.cpp | 14 +-- src/libxrpl/ledger/View.cpp | 9 +- src/libxrpl/ledger/helpers/AMMHelpers.cpp | 9 +- .../ledger/helpers/AccountRootHelpers.cpp | 12 +-- .../ledger/helpers/CredentialHelpers.cpp | 7 +- .../ledger/helpers/DirectoryHelpers.cpp | 19 ++-- src/libxrpl/ledger/helpers/NFTokenHelpers.cpp | 36 ++++---- src/libxrpl/ledger/helpers/OfferHelpers.cpp | 4 +- .../ledger/helpers/PaymentChannelHelpers.cpp | 8 +- .../ledger/helpers/RippleStateHelpers.cpp | 6 +- src/libxrpl/ledger/helpers/VaultHelpers.cpp | 19 ++-- src/libxrpl/tx/ApplyContext.cpp | 19 ++-- src/libxrpl/tx/Transactor.cpp | 9 +- src/libxrpl/tx/invariants/AMMInvariant.cpp | 6 +- src/libxrpl/tx/invariants/FreezeInvariant.cpp | 22 ++--- src/libxrpl/tx/invariants/InvariantCheck.cpp | 70 +++------------ .../tx/invariants/LoanBrokerInvariant.cpp | 7 +- src/libxrpl/tx/invariants/LoanInvariant.cpp | 7 +- src/libxrpl/tx/invariants/MPTInvariant.cpp | 11 +-- src/libxrpl/tx/invariants/NFTInvariant.cpp | 13 +-- .../invariants/PermissionedDEXInvariant.cpp | 7 +- .../PermissionedDomainInvariant.cpp | 8 +- src/libxrpl/tx/invariants/VaultInvariant.cpp | 6 +- src/libxrpl/tx/paths/BookTip.cpp | 4 +- src/libxrpl/tx/paths/DirectStep.cpp | 8 +- src/libxrpl/tx/paths/MPTEndpointStep.cpp | 9 +- src/libxrpl/tx/paths/OfferStream.cpp | 3 +- .../tx/transactors/account/AccountDelete.cpp | 30 +++---- .../tx/transactors/account/AccountSet.cpp | 6 +- .../tx/transactors/account/SetRegularKey.cpp | 7 +- .../tx/transactors/account/SignerListSet.cpp | 5 +- .../tx/transactors/bridge/XChainBridge.cpp | 48 +++-------- .../tx/transactors/check/CheckCancel.cpp | 7 +- .../tx/transactors/check/CheckCash.cpp | 8 +- .../tx/transactors/check/CheckCreate.cpp | 5 +- .../credentials/CredentialAccept.cpp | 7 +- .../credentials/CredentialCreate.cpp | 5 +- .../credentials/CredentialDelete.cpp | 7 +- .../tx/transactors/delegate/DelegateSet.cpp | 7 +- .../tx/transactors/delegate/DelegateUtils.cpp | 5 +- src/libxrpl/tx/transactors/dex/AMMBid.cpp | 6 +- .../tx/transactors/dex/AMMClawback.cpp | 6 +- src/libxrpl/tx/transactors/dex/AMMCreate.cpp | 5 +- src/libxrpl/tx/transactors/dex/AMMDelete.cpp | 7 +- src/libxrpl/tx/transactors/dex/AMMDeposit.cpp | 6 +- src/libxrpl/tx/transactors/dex/AMMVote.cpp | 6 +- .../tx/transactors/dex/AMMWithdraw.cpp | 8 +- .../tx/transactors/dex/OfferCancel.cpp | 7 +- .../tx/transactors/dex/OfferCreate.cpp | 7 +- src/libxrpl/tx/transactors/did/DIDDelete.cpp | 13 +-- src/libxrpl/tx/transactors/did/DIDSet.cpp | 7 +- .../tx/transactors/escrow/EscrowCancel.cpp | 6 +- .../tx/transactors/escrow/EscrowCreate.cpp | 5 +- .../tx/transactors/escrow/EscrowFinish.cpp | 6 +- .../lending/LoanBrokerCoverClawback.cpp | 6 +- .../lending/LoanBrokerCoverDeposit.cpp | 7 +- .../lending/LoanBrokerCoverWithdraw.cpp | 7 +- .../transactors/lending/LoanBrokerDelete.cpp | 7 +- .../tx/transactors/lending/LoanBrokerSet.cpp | 5 +- .../tx/transactors/lending/LoanDelete.cpp | 7 +- .../tx/transactors/lending/LoanManage.cpp | 7 +- .../tx/transactors/lending/LoanPay.cpp | 6 +- .../tx/transactors/lending/LoanSet.cpp | 5 +- .../tx/transactors/nft/NFTokenAcceptOffer.cpp | 15 ++-- .../tx/transactors/nft/NFTokenBurn.cpp | 7 +- .../tx/transactors/nft/NFTokenCancelOffer.cpp | 7 +- .../tx/transactors/nft/NFTokenCreateOffer.cpp | 7 +- .../tx/transactors/nft/NFTokenMint.cpp | 6 +- .../tx/transactors/nft/NFTokenModify.cpp | 7 +- .../tx/transactors/oracle/OracleDelete.cpp | 9 +- .../tx/transactors/oracle/OracleSet.cpp | 5 +- .../tx/transactors/payment/DepositPreauth.cpp | 5 +- .../tx/transactors/payment/Payment.cpp | 5 +- .../payment_channel/PaymentChannelClaim.cpp | 6 +- .../payment_channel/PaymentChannelCreate.cpp | 5 +- .../payment_channel/PaymentChannelFund.cpp | 7 +- .../PermissionedDomainDelete.cpp | 7 +- .../PermissionedDomainSet.cpp | 5 +- src/libxrpl/tx/transactors/system/Batch.cpp | 6 +- src/libxrpl/tx/transactors/system/Change.cpp | 5 +- .../tx/transactors/system/LedgerStateFix.cpp | 6 +- .../tx/transactors/system/TicketCreate.cpp | 5 +- src/libxrpl/tx/transactors/token/Clawback.cpp | 6 +- .../tx/transactors/token/MPTokenAuthorize.cpp | 9 +- .../token/MPTokenIssuanceCreate.cpp | 5 +- .../token/MPTokenIssuanceDestroy.cpp | 7 +- .../transactors/token/MPTokenIssuanceSet.cpp | 8 +- src/libxrpl/tx/transactors/token/TrustSet.cpp | 6 +- .../tx/transactors/vault/VaultClawback.cpp | 12 +-- .../tx/transactors/vault/VaultCreate.cpp | 5 +- .../tx/transactors/vault/VaultDelete.cpp | 7 +- .../tx/transactors/vault/VaultDeposit.cpp | 6 +- src/libxrpl/tx/transactors/vault/VaultSet.cpp | 7 +- .../tx/transactors/vault/VaultWithdraw.cpp | 6 +- src/test/app/CheckMPT_test.cpp | 7 +- src/test/app/Check_test.cpp | 7 +- src/test/app/FlowMPT_test.cpp | 51 ++++++----- src/test/app/Flow_test.cpp | 9 +- src/test/app/Invariants_test.cpp | 2 +- src/test/app/OfferMPT_test.cpp | 45 ++++------ src/test/app/Offer_test.cpp | 37 ++++---- src/test/app/PayChan_test.cpp | 10 +-- src/test/app/XChain_test.cpp | 10 +-- src/test/jtx/Env.h | 4 +- src/test/jtx/PathSet.h | 4 +- src/test/jtx/TestHelpers.h | 2 +- src/test/jtx/impl/Env.cpp | 4 +- src/test/jtx/impl/TestHelpers.cpp | 2 +- src/test/jtx/impl/owners.cpp | 4 +- src/test/ledger/View_test.cpp | 4 +- src/xrpld/app/misc/NetworkOPs.cpp | 2 +- src/xrpld/app/misc/TxQ.h | 8 +- src/xrpld/app/misc/detail/TxQ.cpp | 12 ++- src/xrpld/consensus/DisputedTx.h | 2 +- src/xrpld/rpc/detail/AssetCache.cpp | 2 +- src/xrpld/rpc/detail/RPCHelpers.cpp | 8 +- src/xrpld/rpc/detail/RPCHelpers.h | 7 +- src/xrpld/rpc/detail/TransactionSign.cpp | 9 +- src/xrpld/rpc/detail/TrustLine.cpp | 22 ++--- src/xrpld/rpc/detail/TrustLine.h | 8 +- .../rpc/handlers/account/AccountChannels.cpp | 5 +- .../rpc/handlers/account/AccountLines.cpp | 3 +- .../rpc/handlers/account/AccountOffers.cpp | 7 +- .../rpc/handlers/account/GatewayBalances.cpp | 2 +- .../rpc/handlers/account/NoRippleCheck.cpp | 86 +++++++++---------- src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp | 2 +- .../handlers/orderbook/DepositAuthorized.cpp | 4 +- .../handlers/orderbook/GetAggregatePrice.cpp | 2 +- .../rpc/handlers/orderbook/NFTOffersHelpers.h | 14 +-- .../rpc/handlers/transaction/Simulate.cpp | 2 +- 273 files changed, 719 insertions(+), 1475 deletions(-) diff --git a/cmake/scripts/codegen/templates/LedgerEntry.h.mako b/cmake/scripts/codegen/templates/LedgerEntry.h.mako index 31029cd311..63f5f39ef9 100644 --- a/cmake/scripts/codegen/templates/LedgerEntry.h.mako +++ b/cmake/scripts/codegen/templates/LedgerEntry.h.mako @@ -33,7 +33,7 @@ public: * @brief Construct a ${name} ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit ${name}(std::shared_ptr sle) + explicit ${name}(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -168,7 +168,7 @@ ${field['typeData']['setter_type']} ${field['paramName']}${',' if i < len(requir * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - ${name}Builder(std::shared_ptr sle) + ${name}Builder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ${tag}) { diff --git a/include/xrpl/basics/TaggedCache.h b/include/xrpl/basics/TaggedCache.h index fca9eabde2..380b7c687f 100644 --- a/include/xrpl/basics/TaggedCache.h +++ b/include/xrpl/basics/TaggedCache.h @@ -157,7 +157,7 @@ public: /** Fetch an item from the cache. If the digest was not found, Handler will be called with this signature: - std::shared_ptr(void) + SLE::const_pointer(void) */ template SharedPointerType diff --git a/include/xrpl/ledger/ApplyView.h b/include/xrpl/ledger/ApplyView.h index f825311e1d..362eae0f79 100644 --- a/include/xrpl/ledger/ApplyView.h +++ b/include/xrpl/ledger/ApplyView.h @@ -123,7 +123,7 @@ private: bool preserveOrder, Keylet const& directory, uint256 const& key, - std::function const&)> const& describe); + std::function const& describe); public: ApplyView() = default; @@ -153,7 +153,7 @@ public: @return `nullptr` if the key is not present */ - virtual std::shared_ptr + virtual SLE::pointer peek(Keylet const& k) = 0; /** Remove a peeked SLE. @@ -168,7 +168,7 @@ public: The key is no longer associated with the SLE. */ virtual void - erase(std::shared_ptr const& sle) = 0; + erase(SLE::ref sle) = 0; /** Insert a new state SLE @@ -189,7 +189,7 @@ public: @note The key is taken from the SLE */ virtual void - insert(std::shared_ptr const& sle) = 0; + insert(SLE::ref sle) = 0; /** Indicate changes to a peeked SLE @@ -208,7 +208,7 @@ public: */ /** @{ */ virtual void - update(std::shared_ptr const& sle) = 0; + update(SLE::ref sle) = 0; //-------------------------------------------------------------------------- @@ -301,7 +301,7 @@ public: dirAppend( Keylet const& directory, Keylet const& key, - std::function const&)> const& describe) + std::function const& describe) { if (key.type != ltOFFER) { @@ -340,7 +340,7 @@ public: dirInsert( Keylet const& directory, uint256 const& key, - std::function const&)> const& describe) + std::function const& describe) { return dirAdd(false, directory, key, describe); } @@ -349,7 +349,7 @@ public: dirInsert( Keylet const& directory, Keylet const& key, - std::function const&)> const& describe) + std::function const& describe) { return dirAdd(false, directory, key.key, describe); } @@ -411,7 +411,7 @@ createRoot( ApplyView& view, Keylet const& directory, uint256 const& key, - std::function const&)> const& describe); + std::function const& describe); auto findPreviousPage(ApplyView& view, Keylet const& directory, SLE::ref start); @@ -434,7 +434,7 @@ insertPage( SLE::ref next, uint256 const& key, Keylet const& directory, - std::function const&)> const& describe); + std::function const& describe); } // namespace directory } // namespace xrpl diff --git a/include/xrpl/ledger/ApplyViewImpl.h b/include/xrpl/ledger/ApplyViewImpl.h index 7f790f2be5..1245568630 100644 --- a/include/xrpl/ledger/ApplyViewImpl.h +++ b/include/xrpl/ledger/ApplyViewImpl.h @@ -67,8 +67,8 @@ public: std::function const& before, - std::shared_ptr const& after)> const& func); + SLE::const_ref before, + SLE::const_ref after)> const& func); private: std::optional deliver_; diff --git a/include/xrpl/ledger/BookDirs.h b/include/xrpl/ledger/BookDirs.h index 7eaede14ec..36798934da 100644 --- a/include/xrpl/ledger/BookDirs.h +++ b/include/xrpl/ledger/BookDirs.h @@ -11,13 +11,13 @@ private: uint256 const root_; uint256 const nextQuality_; uint256 const key_; - std::shared_ptr sle_ = nullptr; + SLE::const_pointer sle_ = nullptr; unsigned int entry_ = 0; uint256 index_; public: class const_iterator; // NOLINT(readability-identifier-naming) - using value_type = std::shared_ptr; + using value_type = SLE::const_pointer; BookDirs(ReadView const&, Book const&); @@ -76,7 +76,7 @@ private: uint256 nextQuality_; uint256 key_; uint256 curKey_; - std::shared_ptr sle_; + SLE::const_pointer sle_; unsigned int entry_ = 0; uint256 index_; std::optional mutable cache_; diff --git a/include/xrpl/ledger/CachedView.h b/include/xrpl/ledger/CachedView.h index 34a75e4c07..462db48ee3 100644 --- a/include/xrpl/ledger/CachedView.h +++ b/include/xrpl/ledger/CachedView.h @@ -36,7 +36,7 @@ public: bool exists(Keylet const& k) const override; - std::shared_ptr + SLE::const_pointer read(Keylet const& k) const override; bool diff --git a/include/xrpl/ledger/Dir.h b/include/xrpl/ledger/Dir.h index cfbef357b1..d305e21938 100644 --- a/include/xrpl/ledger/Dir.h +++ b/include/xrpl/ledger/Dir.h @@ -22,12 +22,12 @@ class Dir private: ReadView const* view_ = nullptr; Keylet root_; - std::shared_ptr sle_; + SLE::const_pointer sle_; STVector256 const* indexes_ = nullptr; public: class ConstIterator; - using value_type = std::shared_ptr; + using value_type = SLE::const_pointer; Dir(ReadView const&, Keylet const&); @@ -102,7 +102,7 @@ private: Keylet page_; uint256 index_; std::optional mutable cache_; - std::shared_ptr sle_; + SLE::const_pointer sle_; STVector256 const* indexes_ = nullptr; std::vector::const_iterator it_; }; diff --git a/include/xrpl/ledger/Ledger.h b/include/xrpl/ledger/Ledger.h index 351f7d80e5..5f7d79c61d 100644 --- a/include/xrpl/ledger/Ledger.h +++ b/include/xrpl/ledger/Ledger.h @@ -166,7 +166,7 @@ public: std::optional succ(uint256 const& key, std::optional const& last = std::nullopt) const override; - std::shared_ptr + SLE::const_pointer read(Keylet const& k) const override; std::unique_ptr @@ -202,16 +202,16 @@ public: // void - rawErase(std::shared_ptr const& sle) override; + rawErase(SLE::ref sle) override; void - rawInsert(std::shared_ptr const& sle) override; + rawInsert(SLE::ref sle) override; void rawErase(uint256 const& key); void - rawReplace(std::shared_ptr const& sle) override; + rawReplace(SLE::ref sle) override; void rawDestroyXRP(XRPAmount const& fee) override @@ -361,7 +361,7 @@ public: bool isVotingLedger() const; - std::shared_ptr + SLE::pointer peek(Keylet const& k) const; private: diff --git a/include/xrpl/ledger/OpenView.h b/include/xrpl/ledger/OpenView.h index 18d1a9399c..4ba2a7759b 100644 --- a/include/xrpl/ledger/OpenView.h +++ b/include/xrpl/ledger/OpenView.h @@ -197,7 +197,7 @@ public: std::optional succ(key_type const& key, std::optional const& last = std::nullopt) const override; - std::shared_ptr + SLE::const_pointer read(Keylet const& k) const override; std::unique_ptr @@ -224,13 +224,13 @@ public: // RawView void - rawErase(std::shared_ptr const& sle) override; + rawErase(SLE::ref sle) override; void - rawInsert(std::shared_ptr const& sle) override; + rawInsert(SLE::ref sle) override; void - rawReplace(std::shared_ptr const& sle) override; + rawReplace(SLE::ref sle) override; void rawDestroyXRP(XRPAmount const& fee) override; diff --git a/include/xrpl/ledger/RawView.h b/include/xrpl/ledger/RawView.h index cfcf807e13..cf61c3e814 100644 --- a/include/xrpl/ledger/RawView.h +++ b/include/xrpl/ledger/RawView.h @@ -25,7 +25,7 @@ public: can calculate metadata. */ virtual void - rawErase(std::shared_ptr const& sle) = 0; + rawErase(SLE::ref sle) = 0; /** Unconditionally insert a state item. @@ -39,7 +39,7 @@ public: @note The key is taken from the SLE */ virtual void - rawInsert(std::shared_ptr const& sle) = 0; + rawInsert(SLE::ref sle) = 0; /** Unconditionally replace a state item. @@ -54,7 +54,7 @@ public: @note The key is taken from the SLE */ virtual void - rawReplace(std::shared_ptr const& sle) = 0; + rawReplace(SLE::ref sle) = 0; /** Destroy XRP. diff --git a/include/xrpl/ledger/ReadView.h b/include/xrpl/ledger/ReadView.h index 4f9bf9c31d..f4ee7e6fd2 100644 --- a/include/xrpl/ledger/ReadView.h +++ b/include/xrpl/ledger/ReadView.h @@ -34,9 +34,9 @@ public: using key_type = uint256; - using mapped_type = std::shared_ptr; + using mapped_type = SLE::const_pointer; - struct SlesType : detail::ReadViewFwdRange> + struct SlesType : detail::ReadViewFwdRange { explicit SlesType(ReadView const& view); [[nodiscard]] Iterator @@ -143,7 +143,7 @@ public: @return `nullptr` if the key is not present or if the type does not match. */ - [[nodiscard]] virtual std::shared_ptr + [[nodiscard]] virtual SLE::const_pointer read(Keylet const& k) const = 0; // Accounts in a payment are not allowed to use assets acquired during that diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 0d76c98a73..255413e459 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -135,7 +134,7 @@ areCompatible( dirLink( ApplyView& view, AccountID const& owner, - std::shared_ptr& object, + SLE::pointer& object, SF_UINT64 const& node = sfOwnerNode); /** Checks that can withdraw funds from an object to itself or a destination. @@ -215,8 +214,8 @@ doWithdraw( * (if should not be skipped) and if the entry should be skipped. The status * is always tesSUCCESS if the entry should be skipped. */ -using EntryDeleter = std::function< - std::pair(LedgerEntryType, uint256 const&, std::shared_ptr&)>; +using EntryDeleter = + std::function(LedgerEntryType, uint256 const&, SLE::pointer&)>; /** Cleanup owner directory entries on account delete. * Used for a regular and AMM accounts deletion. The caller * has to provide the deleter function, which handles details of diff --git a/include/xrpl/ledger/detail/ApplyStateTable.h b/include/xrpl/ledger/detail/ApplyStateTable.h index 7b18f742b4..f40e3d0d1c 100644 --- a/include/xrpl/ledger/detail/ApplyStateTable.h +++ b/include/xrpl/ledger/detail/ApplyStateTable.h @@ -8,8 +8,6 @@ #include #include -#include - namespace xrpl::detail { // Helper class that buffers modifications @@ -26,7 +24,7 @@ private: Modify, }; - using items_t = std::map>>; + using items_t = std::map>; items_t items_; XRPAmount dropsDestroyed_{0}; @@ -60,10 +58,10 @@ public: [[nodiscard]] std::optional succ(ReadView const& base, key_type const& key, std::optional const& last) const; - [[nodiscard]] std::shared_ptr + [[nodiscard]] SLE::const_pointer read(ReadView const& base, Keylet const& k) const; - std::shared_ptr + SLE::pointer peek(ReadView const& base, Keylet const& k); [[nodiscard]] std::size_t @@ -75,23 +73,23 @@ public: std::function const& before, - std::shared_ptr const& after)> const& func) const; + SLE::const_ref before, + SLE::const_ref after)> const& func) const; void - erase(ReadView const& base, std::shared_ptr const& sle); + erase(ReadView const& base, SLE::ref sle); void - rawErase(ReadView const& base, std::shared_ptr const& sle); + rawErase(ReadView const& base, SLE::ref sle); void - insert(ReadView const& base, std::shared_ptr const& sle); + insert(ReadView const& base, SLE::ref sle); void - update(ReadView const& base, std::shared_ptr const& sle); + update(ReadView const& base, SLE::ref sle); void - replace(ReadView const& base, std::shared_ptr const& sle); + replace(ReadView const& base, SLE::ref sle); void destroyXRP(XRPAmount const& fee); @@ -104,12 +102,12 @@ public: } private: - using Mods = hash_map>; + using Mods = hash_map; static void - threadItem(TxMeta& meta, std::shared_ptr const& to); + threadItem(TxMeta& meta, SLE::ref to); - std::shared_ptr + SLE::pointer getForMod(ReadView const& base, key_type const& key, Mods& mods, beast::Journal j); void @@ -119,7 +117,7 @@ private: threadOwners( ReadView const& base, TxMeta& meta, - std::shared_ptr const& sle, + SLE::const_ref sle, Mods& mods, beast::Journal j); }; diff --git a/include/xrpl/ledger/detail/ApplyViewBase.h b/include/xrpl/ledger/detail/ApplyViewBase.h index 558c9e5d4d..d6493c46a8 100644 --- a/include/xrpl/ledger/detail/ApplyViewBase.h +++ b/include/xrpl/ledger/detail/ApplyViewBase.h @@ -40,7 +40,7 @@ public: [[nodiscard]] std::optional succ(key_type const& key, std::optional const& last = std::nullopt) const override; - [[nodiscard]] std::shared_ptr + [[nodiscard]] SLE::const_pointer read(Keylet const& k) const override; [[nodiscard]] std::unique_ptr @@ -69,28 +69,28 @@ public: [[nodiscard]] ApplyFlags flags() const override; - std::shared_ptr + SLE::pointer peek(Keylet const& k) override; void - erase(std::shared_ptr const& sle) override; + erase(SLE::ref sle) override; void - insert(std::shared_ptr const& sle) override; + insert(SLE::ref sle) override; void - update(std::shared_ptr const& sle) override; + update(SLE::ref sle) override; // RawView void - rawErase(std::shared_ptr const& sle) override; + rawErase(SLE::ref sle) override; void - rawInsert(std::shared_ptr const& sle) override; + rawInsert(SLE::ref sle) override; void - rawReplace(std::shared_ptr const& sle) override; + rawReplace(SLE::ref sle) override; void rawDestroyXRP(XRPAmount const& feeDrops) override; diff --git a/include/xrpl/ledger/detail/RawStateTable.h b/include/xrpl/ledger/detail/RawStateTable.h index e4329bf6fc..d2567e34f1 100644 --- a/include/xrpl/ledger/detail/RawStateTable.h +++ b/include/xrpl/ledger/detail/RawStateTable.h @@ -49,15 +49,15 @@ public: succ(ReadView const& base, key_type const& key, std::optional const& last) const; void - erase(std::shared_ptr const& sle); + erase(SLE::ref sle); void - insert(std::shared_ptr const& sle); + insert(SLE::ref sle); void - replace(std::shared_ptr const& sle); + replace(SLE::ref sle); - [[nodiscard]] std::shared_ptr + [[nodiscard]] SLE::const_pointer read(ReadView const& base, Keylet const& k) const; void @@ -84,10 +84,10 @@ private: struct SleAction { Action action; - std::shared_ptr sle; + SLE::pointer sle; // Constructor needed for emplacement in std::map - SleAction(Action action, std::shared_ptr const& sle) : action(action), sle(sle) + SleAction(Action action, SLE::pointer sle) : action(action), sle(std::move(sle)) { } }; diff --git a/include/xrpl/ledger/helpers/AMMHelpers.h b/include/xrpl/ledger/helpers/AMMHelpers.h index a146ef753b..61d6e9d2fb 100644 --- a/include/xrpl/ledger/helpers/AMMHelpers.h +++ b/include/xrpl/ledger/helpers/AMMHelpers.h @@ -792,7 +792,7 @@ deleteAMMAccount(Sandbox& view, Asset const& asset, Asset const& asset2, beast:: void initializeFeeAuctionVote( ApplyView& view, - std::shared_ptr& ammSle, + SLE::pointer& ammSle, AccountID const& account, Asset const& lptAsset, std::uint16_t tfee); @@ -812,7 +812,7 @@ Expected verifyAndAdjustLPTokenBalance( Sandbox& sb, STAmount const& lpTokens, - std::shared_ptr& ammSle, + SLE::pointer& ammSle, AccountID const& account); } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index 353c27fe41..c02cad98d8 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -9,7 +9,6 @@ #include #include -#include #include #include @@ -36,11 +35,7 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, /** Adjust the owner count up or down. */ void -adjustOwnerCount( - ApplyView& view, - std::shared_ptr const& sle, - std::int32_t amount, - beast::Journal j); +adjustOwnerCount(ApplyView& view, SLE::ref sle, std::int32_t amount, beast::Journal j); /** Returns IOU issuer transfer fee as Rate. Rate specifies * the fee as fractions of 1 billion. For example, 1% transfer rate @@ -76,9 +71,7 @@ getPseudoAccountFields(); - null pointer */ [[nodiscard]] bool -isPseudoAccount( - std::shared_ptr sleAcct, - std::set const& pseudoFieldFilter = {}); +isPseudoAccount(SLE::const_pointer sleAcct, std::set const& pseudoFieldFilter = {}); /** Convenience overload that reads the account from the view. */ [[nodiscard]] inline bool @@ -98,7 +91,7 @@ isPseudoAccount( * before using a field. The amendment check is **not** performed in * createPseudoAccount. */ -[[nodiscard]] Expected, TER> +[[nodiscard]] Expected createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField); /** Checks the destination and tag. diff --git a/include/xrpl/ledger/helpers/CredentialHelpers.h b/include/xrpl/ledger/helpers/CredentialHelpers.h index e06d225934..549644764f 100644 --- a/include/xrpl/ledger/helpers/CredentialHelpers.h +++ b/include/xrpl/ledger/helpers/CredentialHelpers.h @@ -23,7 +23,7 @@ checkExpired(SLE const& sleCredential, NetClock::time_point const& closed); // Actually remove a credentials object from the ledger [[nodiscard]] TER -deleteSLE(ApplyView& view, std::shared_ptr const& sleCredential, beast::Journal j); +deleteSLE(ApplyView& view, SLE::ref sleCredential, beast::Journal j); // Amendment and parameters checks for sfCredentialIDs field NotTEC @@ -70,7 +70,7 @@ verifyDepositPreauth( ApplyView& view, AccountID const& src, AccountID const& dst, - std::shared_ptr const& sleDst, + SLE::const_ref sleDst, beast::Journal j); } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/DelegateHelpers.h b/include/xrpl/ledger/helpers/DelegateHelpers.h index 78ccc46d0b..9cdad7173d 100644 --- a/include/xrpl/ledger/helpers/DelegateHelpers.h +++ b/include/xrpl/ledger/helpers/DelegateHelpers.h @@ -15,7 +15,7 @@ namespace xrpl { * if not. */ NotTEC -checkTxPermission(std::shared_ptr const& delegate, STTx const& tx); +checkTxPermission(SLE::const_ref delegate, STTx const& tx); /** * Load the granular permissions granted to the delegate account for the @@ -28,7 +28,7 @@ checkTxPermission(std::shared_ptr const& delegate, STTx const& tx); */ void loadGranularPermission( - std::shared_ptr const& delegate, + SLE::const_ref delegate, TxType const& type, std::unordered_set& granularPermissions); diff --git a/include/xrpl/ledger/helpers/DirectoryHelpers.h b/include/xrpl/ledger/helpers/DirectoryHelpers.h index 2ae188182d..a0be52df99 100644 --- a/include/xrpl/ledger/helpers/DirectoryHelpers.h +++ b/include/xrpl/ledger/helpers/DirectoryHelpers.h @@ -115,7 +115,7 @@ bool cdirFirst( ReadView const& view, uint256 const& root, - std::shared_ptr& page, + SLE::const_pointer& page, unsigned int& index, uint256& entry); @@ -123,7 +123,7 @@ bool dirFirst( ApplyView& view, uint256 const& root, - std::shared_ptr& page, + SLE::pointer& page, unsigned int& index, uint256& entry); /** @} */ @@ -147,7 +147,7 @@ bool cdirNext( ReadView const& view, uint256 const& root, - std::shared_ptr& page, + SLE::const_pointer& page, unsigned int& index, uint256& entry); @@ -155,17 +155,14 @@ bool dirNext( ApplyView& view, uint256 const& root, - std::shared_ptr& page, + SLE::pointer& page, unsigned int& index, uint256& entry); /** @} */ /** Iterate all items in the given directory. */ void -forEachItem( - ReadView const& view, - Keylet const& root, - std::function const&)> const& f); +forEachItem(ReadView const& view, Keylet const& root, std::function const& f); /** Iterate all items after an item in the given directory. @param after The key of the item to start after @@ -180,14 +177,11 @@ forEachItemAfter( uint256 const& after, std::uint64_t const hint, unsigned int limit, - std::function const&)> const& f); + std::function const& f); /** Iterate all items in an account's owner directory. */ inline void -forEachItem( - ReadView const& view, - AccountID const& id, - std::function const&)> const& f) +forEachItem(ReadView const& view, AccountID const& id, std::function const& f) { forEachItem(view, keylet::ownerDir(id), f); } @@ -205,7 +199,7 @@ forEachItemAfter( uint256 const& after, std::uint64_t const hint, unsigned int limit, - std::function const&)> const& f) + std::function const& f) { return forEachItemAfter(view, keylet::ownerDir(id), after, hint, limit, f); } diff --git a/include/xrpl/ledger/helpers/EscrowHelpers.h b/include/xrpl/ledger/helpers/EscrowHelpers.h index 859981cf05..bdb83230eb 100644 --- a/include/xrpl/ledger/helpers/EscrowHelpers.h +++ b/include/xrpl/ledger/helpers/EscrowHelpers.h @@ -18,7 +18,7 @@ TER escrowUnlockApplyHelper( ApplyView& view, Rate lockedRate, - std::shared_ptr const& sleDest, + SLE::ref sleDest, STAmount const& xrpBalance, STAmount const& amount, AccountID const& issuer, @@ -32,7 +32,7 @@ inline TER escrowUnlockApplyHelper( ApplyView& view, Rate lockedRate, - std::shared_ptr const& sleDest, + SLE::ref sleDest, STAmount const& xrpBalance, STAmount const& amount, AccountID const& issuer, @@ -162,7 +162,7 @@ inline TER escrowUnlockApplyHelper( ApplyView& view, Rate lockedRate, - std::shared_ptr const& sleDest, + SLE::ref sleDest, STAmount const& xrpBalance, STAmount const& amount, AccountID const& issuer, diff --git a/include/xrpl/ledger/helpers/NFTokenHelpers.h b/include/xrpl/ledger/helpers/NFTokenHelpers.h index 4294e1ca13..362cfe5a8c 100644 --- a/include/xrpl/ledger/helpers/NFTokenHelpers.h +++ b/include/xrpl/ledger/helpers/NFTokenHelpers.h @@ -28,10 +28,9 @@ findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID struct TokenAndPage { STObject token; - std::shared_ptr page; + SLE::pointer page; - TokenAndPage(STObject token, std::shared_ptr page) - : token(std::move(token)), page(std::move(page)) + TokenAndPage(STObject token, SLE::pointer page) : token(std::move(token)), page(std::move(page)) { } }; @@ -47,11 +46,7 @@ TER removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID); TER -removeToken( - ApplyView& view, - AccountID const& owner, - uint256 const& nftokenID, - std::shared_ptr const& page); +removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, SLE::ref page); /** Deletes the given token offer. @@ -63,7 +58,7 @@ removeToken( The offer also consumes one incremental reserve. */ bool -deleteTokenOffer(ApplyView& view, std::shared_ptr const& offer); +deleteTokenOffer(ApplyView& view, SLE::ref offer); /** Repairs the links in an NFTokenPage directory. diff --git a/include/xrpl/ledger/helpers/OfferHelpers.h b/include/xrpl/ledger/helpers/OfferHelpers.h index 9096071811..fc863dff0a 100644 --- a/include/xrpl/ledger/helpers/OfferHelpers.h +++ b/include/xrpl/ledger/helpers/OfferHelpers.h @@ -5,8 +5,6 @@ #include #include -#include - namespace xrpl { /** Delete an offer. @@ -23,6 +21,6 @@ namespace xrpl { */ // [[nodiscard]] // nodiscard commented out so Flow, BookTip and others compile. TER -offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j); +offerDelete(ApplyView& view, SLE::ref sle, beast::Journal j); } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/PaymentChannelHelpers.h b/include/xrpl/ledger/helpers/PaymentChannelHelpers.h index 24838f1331..810907b0af 100644 --- a/include/xrpl/ledger/helpers/PaymentChannelHelpers.h +++ b/include/xrpl/ledger/helpers/PaymentChannelHelpers.h @@ -8,10 +8,6 @@ namespace xrpl { TER -closeChannel( - std::shared_ptr const& slep, - ApplyView& view, - uint256 const& key, - beast::Journal j); +closeChannel(SLE::ref slep, ApplyView& view, uint256 const& key, beast::Journal j); } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/RippleStateHelpers.h b/include/xrpl/ledger/helpers/RippleStateHelpers.h index 2616a6d5c9..3aaaa541fd 100644 --- a/include/xrpl/ledger/helpers/RippleStateHelpers.h +++ b/include/xrpl/ledger/helpers/RippleStateHelpers.h @@ -154,7 +154,7 @@ trustCreate( [[nodiscard]] TER trustDelete( ApplyView& view, - std::shared_ptr const& sleRippleState, + SLE::ref sleRippleState, AccountID const& uLowAccountID, AccountID const& uHighAccountID, beast::Journal j); @@ -248,7 +248,7 @@ removeEmptyHolding( [[nodiscard]] TER deleteAMMTrustLine( ApplyView& view, - std::shared_ptr sleState, + SLE::pointer sleState, std::optional const& ammAccountID, beast::Journal j); @@ -258,7 +258,7 @@ deleteAMMTrustLine( [[nodiscard]] TER deleteAMMMPToken( ApplyView& view, - std::shared_ptr sleMPT, + SLE::pointer sleMPT, AccountID const& ammAccountID, beast::Journal j); diff --git a/include/xrpl/ledger/helpers/VaultHelpers.h b/include/xrpl/ledger/helpers/VaultHelpers.h index 29270d913f..2344b4de77 100644 --- a/include/xrpl/ledger/helpers/VaultHelpers.h +++ b/include/xrpl/ledger/helpers/VaultHelpers.h @@ -5,7 +5,6 @@ #include #include -#include #include namespace xrpl { @@ -21,10 +20,7 @@ namespace xrpl { @return The number of shares, or nullopt on error. */ [[nodiscard]] std::optional -assetsToSharesDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& assets); +assetsToSharesDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& assets); /** From the perspective of a vault, return the number of assets to take from depositor when they receive a fixed amount of shares. Note, since shares are @@ -37,10 +33,7 @@ assetsToSharesDeposit( @return The number of assets, or nullopt on error. */ [[nodiscard]] std::optional -sharesToAssetsDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& shares); +sharesToAssetsDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& shares); /** Controls whether to truncate shares instead of rounding. */ enum class TruncateShares : bool { No = false, Yes = true }; @@ -69,8 +62,8 @@ enum class WaiveUnrealizedLoss : bool { No = false, Yes = true }; */ [[nodiscard]] std::optional assetsToSharesWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, + SLE::const_ref vault, + SLE::const_ref issuance, STAmount const& assets, TruncateShares truncate = TruncateShares::No, WaiveUnrealizedLoss waive = WaiveUnrealizedLoss::No); @@ -89,8 +82,8 @@ assetsToSharesWithdraw( */ [[nodiscard]] std::optional sharesToAssetsWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, + SLE::const_ref vault, + SLE::const_ref issuance, STAmount const& shares, WaiveUnrealizedLoss waive = WaiveUnrealizedLoss::No); @@ -104,9 +97,6 @@ sharesToAssetsWithdraw( both the share MPTID and the outstanding-amount total. */ [[nodiscard]] bool -isSoleShareholder( - ReadView const& view, - AccountID const& account, - std::shared_ptr const& issuance); +isSoleShareholder(ReadView const& view, AccountID const& account, SLE::const_ref issuance); } // namespace xrpl diff --git a/include/xrpl/protocol_autogen/LedgerEntryBase.h b/include/xrpl/protocol_autogen/LedgerEntryBase.h index ad513992c7..5758adbb24 100644 --- a/include/xrpl/protocol_autogen/LedgerEntryBase.h +++ b/include/xrpl/protocol_autogen/LedgerEntryBase.h @@ -27,7 +27,7 @@ public: * @brief Construct a ledger entry wrapper from an existing SLE object. * @param sle The underlying serialized ledger entry to wrap */ - explicit LedgerEntryBase(std::shared_ptr sle) : sle_(std::move(sle)) + explicit LedgerEntryBase(SLE::const_pointer sle) : sle_(std::move(sle)) { } @@ -151,7 +151,7 @@ public: * @return A constant reference to the underlying SLE object */ [[nodiscard]] - std::shared_ptr + SLE::const_pointer getSle() const { return sle_; @@ -159,7 +159,7 @@ public: protected: /** @brief The underlying serialized ledger entry being wrapped. */ - std::shared_ptr sle_; + SLE::const_pointer sle_; }; } // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/AMM.h b/include/xrpl/protocol_autogen/ledger_entries/AMM.h index 529e3f4df7..11fd3738c9 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/AMM.h +++ b/include/xrpl/protocol_autogen/ledger_entries/AMM.h @@ -33,7 +33,7 @@ public: * @brief Construct a AMM ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit AMM(std::shared_ptr sle) + explicit AMM(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -256,7 +256,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - AMMBuilder(std::shared_ptr sle) + AMMBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltAMM) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h b/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h index e20259330d..f9a12a027f 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h +++ b/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h @@ -33,7 +33,7 @@ public: * @brief Construct a AccountRoot ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit AccountRoot(std::shared_ptr sle) + explicit AccountRoot(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -555,7 +555,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - AccountRootBuilder(std::shared_ptr sle) + AccountRootBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltACCOUNT_ROOT) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Amendments.h b/include/xrpl/protocol_autogen/ledger_entries/Amendments.h index 4f1b316b99..6a801308ca 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Amendments.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Amendments.h @@ -33,7 +33,7 @@ public: * @brief Construct a Amendments ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Amendments(std::shared_ptr sle) + explicit Amendments(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -166,7 +166,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - AmendmentsBuilder(std::shared_ptr sle) + AmendmentsBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltAMENDMENTS) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Bridge.h b/include/xrpl/protocol_autogen/ledger_entries/Bridge.h index fd7df1977f..2c7479b243 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Bridge.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Bridge.h @@ -33,7 +33,7 @@ public: * @brief Construct a Bridge ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Bridge(std::shared_ptr sle) + explicit Bridge(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -210,7 +210,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - BridgeBuilder(std::shared_ptr sle) + BridgeBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltBRIDGE) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Check.h b/include/xrpl/protocol_autogen/ledger_entries/Check.h index 750270cad9..5b3fd10b92 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Check.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Check.h @@ -33,7 +33,7 @@ public: * @brief Construct a Check ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Check(std::shared_ptr sle) + explicit Check(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -269,7 +269,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - CheckBuilder(std::shared_ptr sle) + CheckBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltCHECK) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Credential.h b/include/xrpl/protocol_autogen/ledger_entries/Credential.h index 5433f00b33..dfce76e45c 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Credential.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Credential.h @@ -33,7 +33,7 @@ public: * @brief Construct a Credential ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Credential(std::shared_ptr sle) + explicit Credential(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -219,7 +219,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - CredentialBuilder(std::shared_ptr sle) + CredentialBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltCREDENTIAL) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/DID.h b/include/xrpl/protocol_autogen/ledger_entries/DID.h index 2f57961be4..ad423377e7 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/DID.h +++ b/include/xrpl/protocol_autogen/ledger_entries/DID.h @@ -33,7 +33,7 @@ public: * @brief Construct a DID ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit DID(std::shared_ptr sle) + explicit DID(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -193,7 +193,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - DIDBuilder(std::shared_ptr sle) + DIDBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltDID) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Delegate.h b/include/xrpl/protocol_autogen/ledger_entries/Delegate.h index 7458b8103a..bfe5f5587a 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Delegate.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Delegate.h @@ -33,7 +33,7 @@ public: * @brief Construct a Delegate ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Delegate(std::shared_ptr sle) + explicit Delegate(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -172,7 +172,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - DelegateBuilder(std::shared_ptr sle) + DelegateBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltDELEGATE) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h b/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h index 42b430a665..069bed6b77 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h +++ b/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h @@ -33,7 +33,7 @@ public: * @brief Construct a DepositPreauth ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit DepositPreauth(std::shared_ptr sle) + explicit DepositPreauth(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -170,7 +170,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - DepositPreauthBuilder(std::shared_ptr sle) + DepositPreauthBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltDEPOSIT_PREAUTH) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h b/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h index e47cb98a26..50659c33f6 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h +++ b/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h @@ -33,7 +33,7 @@ public: * @brief Construct a DirectoryNode ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit DirectoryNode(std::shared_ptr sle) + explicit DirectoryNode(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -431,7 +431,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - DirectoryNodeBuilder(std::shared_ptr sle) + DirectoryNodeBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltDIR_NODE) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Escrow.h b/include/xrpl/protocol_autogen/ledger_entries/Escrow.h index ae8219a4f0..f3c033d26d 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Escrow.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Escrow.h @@ -33,7 +33,7 @@ public: * @brief Construct a Escrow ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Escrow(std::shared_ptr sle) + explicit Escrow(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -363,7 +363,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - EscrowBuilder(std::shared_ptr sle) + EscrowBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltESCROW) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h b/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h index cfd7a591e8..8f43d3b782 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h +++ b/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h @@ -33,7 +33,7 @@ public: * @brief Construct a FeeSettings ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit FeeSettings(std::shared_ptr sle) + explicit FeeSettings(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -285,7 +285,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - FeeSettingsBuilder(std::shared_ptr sle) + FeeSettingsBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltFEE_SETTINGS) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h b/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h index 8c082be685..f1d3684b55 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h +++ b/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h @@ -33,7 +33,7 @@ public: * @brief Construct a LedgerHashes ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit LedgerHashes(std::shared_ptr sle) + explicit LedgerHashes(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -130,7 +130,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - LedgerHashesBuilder(std::shared_ptr sle) + LedgerHashesBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltLEDGER_HASHES) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Loan.h b/include/xrpl/protocol_autogen/ledger_entries/Loan.h index 4408c278db..5d837736ec 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Loan.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Loan.h @@ -33,7 +33,7 @@ public: * @brief Construct a Loan ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Loan(std::shared_ptr sle) + explicit Loan(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -607,7 +607,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - LoanBuilder(std::shared_ptr sle) + LoanBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltLOAN) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h b/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h index b1c6dd8a54..88f05e3433 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h +++ b/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h @@ -33,7 +33,7 @@ public: * @brief Construct a LoanBroker ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit LoanBroker(std::shared_ptr sle) + explicit LoanBroker(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -378,7 +378,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - LoanBrokerBuilder(std::shared_ptr sle) + LoanBrokerBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltLOAN_BROKER) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/MPToken.h b/include/xrpl/protocol_autogen/ledger_entries/MPToken.h index 8176b74fe9..0d394020f7 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/MPToken.h +++ b/include/xrpl/protocol_autogen/ledger_entries/MPToken.h @@ -33,7 +33,7 @@ public: * @brief Construct a MPToken ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit MPToken(std::shared_ptr sle) + explicit MPToken(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -182,7 +182,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - MPTokenBuilder(std::shared_ptr sle) + MPTokenBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltMPTOKEN) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h b/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h index 7f772b1c74..d493ac779c 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h +++ b/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h @@ -33,7 +33,7 @@ public: * @brief Construct a MPTokenIssuance ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit MPTokenIssuance(std::shared_ptr sle) + explicit MPTokenIssuance(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -339,7 +339,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - MPTokenIssuanceBuilder(std::shared_ptr sle) + MPTokenIssuanceBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltMPTOKEN_ISSUANCE) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h b/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h index c0a6ee6cbc..072d3721f9 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h +++ b/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h @@ -33,7 +33,7 @@ public: * @brief Construct a NFTokenOffer ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit NFTokenOffer(std::shared_ptr sle) + explicit NFTokenOffer(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -208,7 +208,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - NFTokenOfferBuilder(std::shared_ptr sle) + NFTokenOfferBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltNFTOKEN_OFFER) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h b/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h index c4190f9068..5e00cb1120 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h +++ b/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h @@ -33,7 +33,7 @@ public: * @brief Construct a NFTokenPage ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit NFTokenPage(std::shared_ptr sle) + explicit NFTokenPage(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -157,7 +157,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - NFTokenPageBuilder(std::shared_ptr sle) + NFTokenPageBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltNFTOKEN_PAGE) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h b/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h index b09135e5f8..7ca9729082 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h +++ b/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h @@ -33,7 +33,7 @@ public: * @brief Construct a NegativeUNL ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit NegativeUNL(std::shared_ptr sle) + explicit NegativeUNL(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -190,7 +190,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - NegativeUNLBuilder(std::shared_ptr sle) + NegativeUNLBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltNEGATIVE_UNL) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Offer.h b/include/xrpl/protocol_autogen/ledger_entries/Offer.h index a6d427cbbd..f51b54cfd2 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Offer.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Offer.h @@ -33,7 +33,7 @@ public: * @brief Construct a Offer ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Offer(std::shared_ptr sle) + explicit Offer(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -259,7 +259,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - OfferBuilder(std::shared_ptr sle) + OfferBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltOFFER) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Oracle.h b/include/xrpl/protocol_autogen/ledger_entries/Oracle.h index 31735a713c..902032f94f 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Oracle.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Oracle.h @@ -33,7 +33,7 @@ public: * @brief Construct a Oracle ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Oracle(std::shared_ptr sle) + explicit Oracle(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -222,7 +222,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - OracleBuilder(std::shared_ptr sle) + OracleBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltORACLE) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h b/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h index 097f287425..61a4e2d044 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h +++ b/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h @@ -33,7 +33,7 @@ public: * @brief Construct a PayChannel ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit PayChannel(std::shared_ptr sle) + explicit PayChannel(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -330,7 +330,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - PayChannelBuilder(std::shared_ptr sle) + PayChannelBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltPAYCHAN) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h b/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h index cc5a1649ef..638dda2420 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h +++ b/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h @@ -33,7 +33,7 @@ public: * @brief Construct a PermissionedDomain ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit PermissionedDomain(std::shared_ptr sle) + explicit PermissionedDomain(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -148,7 +148,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - PermissionedDomainBuilder(std::shared_ptr sle) + PermissionedDomainBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltPERMISSIONED_DOMAIN) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/RippleState.h b/include/xrpl/protocol_autogen/ledger_entries/RippleState.h index cb221bb2ad..e8debfe792 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/RippleState.h +++ b/include/xrpl/protocol_autogen/ledger_entries/RippleState.h @@ -33,7 +33,7 @@ public: * @brief Construct a RippleState ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit RippleState(std::shared_ptr sle) + explicit RippleState(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -278,7 +278,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - RippleStateBuilder(std::shared_ptr sle) + RippleStateBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltRIPPLE_STATE) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/SignerList.h b/include/xrpl/protocol_autogen/ledger_entries/SignerList.h index dfc7eb4506..443e5588f9 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/SignerList.h +++ b/include/xrpl/protocol_autogen/ledger_entries/SignerList.h @@ -33,7 +33,7 @@ public: * @brief Construct a SignerList ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit SignerList(std::shared_ptr sle) + explicit SignerList(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -172,7 +172,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - SignerListBuilder(std::shared_ptr sle) + SignerListBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltSIGNER_LIST) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Ticket.h b/include/xrpl/protocol_autogen/ledger_entries/Ticket.h index ceadd70765..6fa5b57f6c 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Ticket.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Ticket.h @@ -33,7 +33,7 @@ public: * @brief Construct a Ticket ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Ticket(std::shared_ptr sle) + explicit Ticket(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -134,7 +134,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - TicketBuilder(std::shared_ptr sle) + TicketBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltTICKET) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Vault.h b/include/xrpl/protocol_autogen/ledger_entries/Vault.h index 168894d8d2..d1aaeb4ed8 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Vault.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Vault.h @@ -33,7 +33,7 @@ public: * @brief Construct a Vault ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Vault(std::shared_ptr sle) + explicit Vault(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -330,7 +330,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - VaultBuilder(std::shared_ptr sle) + VaultBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltVAULT) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h index 4a3e9c9103..3f8058a4a1 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h +++ b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h @@ -33,7 +33,7 @@ public: * @brief Construct a XChainOwnedClaimID ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit XChainOwnedClaimID(std::shared_ptr sle) + explicit XChainOwnedClaimID(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -187,7 +187,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - XChainOwnedClaimIDBuilder(std::shared_ptr sle) + XChainOwnedClaimIDBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltXCHAIN_OWNED_CLAIM_ID) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h index 542de104ea..e24009a4b7 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h +++ b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h @@ -33,7 +33,7 @@ public: * @brief Construct a XChainOwnedCreateAccountClaimID ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit XChainOwnedCreateAccountClaimID(std::shared_ptr sle) + explicit XChainOwnedCreateAccountClaimID(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -161,7 +161,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - XChainOwnedCreateAccountClaimIDBuilder(std::shared_ptr sle) + XChainOwnedCreateAccountClaimIDBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID) { diff --git a/include/xrpl/tx/ApplyContext.h b/include/xrpl/tx/ApplyContext.h index 910ec6be42..8540037601 100644 --- a/include/xrpl/tx/ApplyContext.h +++ b/include/xrpl/tx/ApplyContext.h @@ -93,8 +93,8 @@ public: std::function const& before, - std::shared_ptr const& after)> const& func); + SLE::const_ref before, + SLE::const_ref after)> const& func); void destroyXRP(XRPAmount const& fee) diff --git a/include/xrpl/tx/Transactor.h b/include/xrpl/tx/Transactor.h index 1440a5097f..86b1e856b3 100644 --- a/include/xrpl/tx/Transactor.h +++ b/include/xrpl/tx/Transactor.h @@ -263,10 +263,7 @@ protected: * to detect deletions. */ virtual void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) = 0; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) = 0; /** Check transaction-specific post-conditions after all entries have * been visited. @@ -368,7 +365,7 @@ private: ReadView const& view, AccountID const& idSigner, AccountID const& idAccount, - std::shared_ptr sleAccount, + SLE::const_pointer sleAccount, beast::Journal const j); static NotTEC checkMultiSign( diff --git a/include/xrpl/tx/invariants/AMMInvariant.h b/include/xrpl/tx/invariants/AMMInvariant.h index 43d9c5ad0a..ee2fb66a1c 100644 --- a/include/xrpl/tx/invariants/AMMInvariant.h +++ b/include/xrpl/tx/invariants/AMMInvariant.h @@ -22,7 +22,7 @@ public: ValidAMM() = default; void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/invariants/FreezeInvariant.h b/include/xrpl/tx/invariants/FreezeInvariant.h index 645f444462..a76eb66497 100644 --- a/include/xrpl/tx/invariants/FreezeInvariant.h +++ b/include/xrpl/tx/invariants/FreezeInvariant.h @@ -22,7 +22,7 @@ class TransfersNotFrozen { struct BalanceChange { - std::shared_ptr const line; + SLE::const_pointer const line; int const balanceChangeSign; }; @@ -35,37 +35,34 @@ class TransfersNotFrozen using ByIssuer = std::map; ByIssuer balanceChanges_; - std::map const> possibleIssuers_; + std::map possibleIssuers_; public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); private: bool - isValidEntry(std::shared_ptr const& before, std::shared_ptr const& after); + isValidEntry(SLE::const_ref before, SLE::const_ref after); static STAmount - calculateBalanceChange( - std::shared_ptr const& before, - std::shared_ptr const& after, - bool isDelete); + calculateBalanceChange(SLE::const_ref before, SLE::const_ref after, bool isDelete); void recordBalance(Issue const& issue, BalanceChange change); void - recordBalanceChanges(std::shared_ptr const& after, STAmount const& balanceChange); + recordBalanceChanges(SLE::const_ref after, STAmount const& balanceChange); - std::shared_ptr + SLE::const_pointer findIssuer(AccountID const& issuerID, ReadView const& view); static bool validateIssuerChanges( - std::shared_ptr const& issuer, + SLE::const_ref issuer, IssuerChanges const& changes, STTx const& tx, beast::Journal const& j, diff --git a/include/xrpl/tx/invariants/InvariantCheck.h b/include/xrpl/tx/invariants/InvariantCheck.h index d4c0154269..9378062726 100644 --- a/include/xrpl/tx/invariants/InvariantCheck.h +++ b/include/xrpl/tx/invariants/InvariantCheck.h @@ -70,10 +70,7 @@ public: * @param after ledger entry after modification by the transaction */ void - visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after); + visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after); /** * @brief called after all ledger entries have been visited to determine @@ -111,7 +108,7 @@ class TransactionFeeCheck { public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); static bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); @@ -131,7 +128,7 @@ class XRPNotCreated public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -151,7 +148,7 @@ class AccountRootsNotDeleted public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -174,11 +171,11 @@ class AccountRootsDeletedClean // deleted, it can still be found. After is used specifically for any checks // that are expected as part of the deletion, such as zeroing out the // balance. - std::vector, std::shared_ptr>> accountsDeleted_; + std::vector> accountsDeleted_; public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); @@ -197,7 +194,7 @@ class XRPBalanceChecks public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -214,7 +211,7 @@ class LedgerEntryTypesMatch public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -232,7 +229,7 @@ class NoXRPTrustLines public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -251,7 +248,7 @@ class NoDeepFreezeTrustLinesWithoutFreeze public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -270,7 +267,7 @@ class NoBadOffers public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -286,7 +283,7 @@ class NoZeroEscrow public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -306,7 +303,7 @@ class ValidNewAccountRoot public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -327,7 +324,7 @@ class ValidClawback public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -347,7 +344,7 @@ class ValidPseudoAccounts public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); @@ -367,7 +364,7 @@ class NoModifiedUnmodifiableFields public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/invariants/LoanBrokerInvariant.h b/include/xrpl/tx/invariants/LoanBrokerInvariant.h index e7d14a638b..684bbff423 100644 --- a/include/xrpl/tx/invariants/LoanBrokerInvariant.h +++ b/include/xrpl/tx/invariants/LoanBrokerInvariant.h @@ -46,7 +46,7 @@ class ValidLoanBroker public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/invariants/LoanInvariant.h b/include/xrpl/tx/invariants/LoanInvariant.h index bda9c51653..3f408d169a 100644 --- a/include/xrpl/tx/invariants/LoanInvariant.h +++ b/include/xrpl/tx/invariants/LoanInvariant.h @@ -23,7 +23,7 @@ class ValidLoan public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/invariants/MPTInvariant.h b/include/xrpl/tx/invariants/MPTInvariant.h index becb752126..b4b76a290f 100644 --- a/include/xrpl/tx/invariants/MPTInvariant.h +++ b/include/xrpl/tx/invariants/MPTInvariant.h @@ -37,7 +37,7 @@ class ValidMPTIssuance public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -65,7 +65,7 @@ class ValidMPTPayment public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/invariants/NFTInvariant.h b/include/xrpl/tx/invariants/NFTInvariant.h index fa056ecbb0..698df05247 100644 --- a/include/xrpl/tx/invariants/NFTInvariant.h +++ b/include/xrpl/tx/invariants/NFTInvariant.h @@ -33,7 +33,7 @@ class ValidNFTokenPage public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -61,7 +61,7 @@ class NFTokenCountTracking public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; diff --git a/include/xrpl/tx/invariants/PermissionedDEXInvariant.h b/include/xrpl/tx/invariants/PermissionedDEXInvariant.h index 654ed3e009..2ec22ded88 100644 --- a/include/xrpl/tx/invariants/PermissionedDEXInvariant.h +++ b/include/xrpl/tx/invariants/PermissionedDEXInvariant.h @@ -18,7 +18,7 @@ class ValidPermissionedDEX public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/invariants/PermissionedDomainInvariant.h b/include/xrpl/tx/invariants/PermissionedDomainInvariant.h index 2475ed8f6b..19edcc0b39 100644 --- a/include/xrpl/tx/invariants/PermissionedDomainInvariant.h +++ b/include/xrpl/tx/invariants/PermissionedDomainInvariant.h @@ -32,7 +32,7 @@ class ValidPermissionedDomain public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/invariants/VaultInvariant.h b/include/xrpl/tx/invariants/VaultInvariant.h index abc256c880..2a9ffc8282 100644 --- a/include/xrpl/tx/invariants/VaultInvariant.h +++ b/include/xrpl/tx/invariants/VaultInvariant.h @@ -154,7 +154,7 @@ public: computeCoarsestScale(std::vector const& numbers); void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/paths/BookTip.h b/include/xrpl/tx/paths/BookTip.h index e06a2da86c..c4bdb0415c 100644 --- a/include/xrpl/tx/paths/BookTip.h +++ b/include/xrpl/tx/paths/BookTip.h @@ -21,7 +21,7 @@ private: uint256 end_; uint256 dir_; uint256 index_; - std::shared_ptr entry_; + SLE::pointer entry_; Quality quality_{}; public: diff --git a/include/xrpl/tx/transactors/account/AccountDelete.h b/include/xrpl/tx/transactors/account/AccountDelete.h index d2cbfa5ad2..16661a4b7c 100644 --- a/include/xrpl/tx/transactors/account/AccountDelete.h +++ b/include/xrpl/tx/transactors/account/AccountDelete.h @@ -29,10 +29,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/account/AccountSet.h b/include/xrpl/tx/transactors/account/AccountSet.h index 002779db64..a40a9ec963 100644 --- a/include/xrpl/tx/transactors/account/AccountSet.h +++ b/include/xrpl/tx/transactors/account/AccountSet.h @@ -33,10 +33,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/account/SetRegularKey.h b/include/xrpl/tx/transactors/account/SetRegularKey.h index a9f1ce715d..6ff9c5aa52 100644 --- a/include/xrpl/tx/transactors/account/SetRegularKey.h +++ b/include/xrpl/tx/transactors/account/SetRegularKey.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/account/SignerListSet.h b/include/xrpl/tx/transactors/account/SignerListSet.h index 760f6e9358..46e3191323 100644 --- a/include/xrpl/tx/transactors/account/SignerListSet.h +++ b/include/xrpl/tx/transactors/account/SignerListSet.h @@ -42,10 +42,7 @@ public: preCompute() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/bridge/XChainBridge.h b/include/xrpl/tx/transactors/bridge/XChainBridge.h index 1033dee188..58a546de2f 100644 --- a/include/xrpl/tx/transactors/bridge/XChainBridge.h +++ b/include/xrpl/tx/transactors/bridge/XChainBridge.h @@ -28,10 +28,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -64,10 +61,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -111,10 +105,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -152,10 +143,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -195,10 +183,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -238,10 +223,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -272,10 +254,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -330,10 +309,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/check/CheckCancel.h b/include/xrpl/tx/transactors/check/CheckCancel.h index 5fdaa7e527..b8e8b6c52d 100644 --- a/include/xrpl/tx/transactors/check/CheckCancel.h +++ b/include/xrpl/tx/transactors/check/CheckCancel.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/check/CheckCash.h b/include/xrpl/tx/transactors/check/CheckCash.h index ad9de1e4c3..7d4e615cfd 100644 --- a/include/xrpl/tx/transactors/check/CheckCash.h +++ b/include/xrpl/tx/transactors/check/CheckCash.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/check/CheckCreate.h b/include/xrpl/tx/transactors/check/CheckCreate.h index e03677e5f5..178fe4707c 100644 --- a/include/xrpl/tx/transactors/check/CheckCreate.h +++ b/include/xrpl/tx/transactors/check/CheckCreate.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/credentials/CredentialAccept.h b/include/xrpl/tx/transactors/credentials/CredentialAccept.h index 97838f8a0a..8630ac3f7f 100644 --- a/include/xrpl/tx/transactors/credentials/CredentialAccept.h +++ b/include/xrpl/tx/transactors/credentials/CredentialAccept.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/credentials/CredentialCreate.h b/include/xrpl/tx/transactors/credentials/CredentialCreate.h index 7493aa4dc5..91b5e829d3 100644 --- a/include/xrpl/tx/transactors/credentials/CredentialCreate.h +++ b/include/xrpl/tx/transactors/credentials/CredentialCreate.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/credentials/CredentialDelete.h b/include/xrpl/tx/transactors/credentials/CredentialDelete.h index 4d9b4ddd18..70fe5cb3d0 100644 --- a/include/xrpl/tx/transactors/credentials/CredentialDelete.h +++ b/include/xrpl/tx/transactors/credentials/CredentialDelete.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/delegate/DelegateSet.h b/include/xrpl/tx/transactors/delegate/DelegateSet.h index c93c48e970..a55524ecff 100644 --- a/include/xrpl/tx/transactors/delegate/DelegateSet.h +++ b/include/xrpl/tx/transactors/delegate/DelegateSet.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -38,7 +35,7 @@ public: // Interface used by AccountDelete static TER - deleteDelegate(ApplyView& view, std::shared_ptr const& sle, beast::Journal j); + deleteDelegate(ApplyView& view, SLE::ref sle, beast::Journal j); }; } // namespace xrpl diff --git a/include/xrpl/tx/transactors/dex/AMMBid.h b/include/xrpl/tx/transactors/dex/AMMBid.h index 9328c48e79..dfa50d06ba 100644 --- a/include/xrpl/tx/transactors/dex/AMMBid.h +++ b/include/xrpl/tx/transactors/dex/AMMBid.h @@ -64,10 +64,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/dex/AMMClawback.h b/include/xrpl/tx/transactors/dex/AMMClawback.h index 7ac03ebb28..6f31480490 100644 --- a/include/xrpl/tx/transactors/dex/AMMClawback.h +++ b/include/xrpl/tx/transactors/dex/AMMClawback.h @@ -29,10 +29,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/dex/AMMCreate.h b/include/xrpl/tx/transactors/dex/AMMCreate.h index 04d6fe6f60..64d2a1e3b1 100644 --- a/include/xrpl/tx/transactors/dex/AMMCreate.h +++ b/include/xrpl/tx/transactors/dex/AMMCreate.h @@ -60,10 +60,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/dex/AMMDelete.h b/include/xrpl/tx/transactors/dex/AMMDelete.h index ff5776e3b7..d3e8cfeeb4 100644 --- a/include/xrpl/tx/transactors/dex/AMMDelete.h +++ b/include/xrpl/tx/transactors/dex/AMMDelete.h @@ -32,10 +32,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/dex/AMMDeposit.h b/include/xrpl/tx/transactors/dex/AMMDeposit.h index 9be53167f2..453046ddad 100644 --- a/include/xrpl/tx/transactors/dex/AMMDeposit.h +++ b/include/xrpl/tx/transactors/dex/AMMDeposit.h @@ -64,10 +64,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/dex/AMMVote.h b/include/xrpl/tx/transactors/dex/AMMVote.h index 1b5946aae1..8defc1369e 100644 --- a/include/xrpl/tx/transactors/dex/AMMVote.h +++ b/include/xrpl/tx/transactors/dex/AMMVote.h @@ -49,10 +49,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/dex/AMMWithdraw.h b/include/xrpl/tx/transactors/dex/AMMWithdraw.h index 9e6eb62d51..6e88320eae 100644 --- a/include/xrpl/tx/transactors/dex/AMMWithdraw.h +++ b/include/xrpl/tx/transactors/dex/AMMWithdraw.h @@ -72,10 +72,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -155,7 +152,7 @@ public: static std::pair deleteAMMAccountIfEmpty( Sandbox& sb, - std::shared_ptr const ammSle, + SLE::pointer const ammSle, STAmount const& lpTokenBalance, Asset const& asset1, Asset const& asset2, diff --git a/include/xrpl/tx/transactors/dex/OfferCancel.h b/include/xrpl/tx/transactors/dex/OfferCancel.h index b2641049e6..2806b6942f 100644 --- a/include/xrpl/tx/transactors/dex/OfferCancel.h +++ b/include/xrpl/tx/transactors/dex/OfferCancel.h @@ -24,10 +24,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/dex/OfferCreate.h b/include/xrpl/tx/transactors/dex/OfferCreate.h index efae5312e2..7faf613d3a 100644 --- a/include/xrpl/tx/transactors/dex/OfferCreate.h +++ b/include/xrpl/tx/transactors/dex/OfferCreate.h @@ -41,10 +41,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -81,7 +78,7 @@ private: TER applyHybrid( Sandbox& sb, - std::shared_ptr sleOffer, + STLedgerEntry::pointer sleOffer, Keylet const& offerIndex, STAmount const& saTakerPays, STAmount const& saTakerGets, diff --git a/include/xrpl/tx/transactors/did/DIDDelete.h b/include/xrpl/tx/transactors/did/DIDDelete.h index c750d4c95e..94615f51b6 100644 --- a/include/xrpl/tx/transactors/did/DIDDelete.h +++ b/include/xrpl/tx/transactors/did/DIDDelete.h @@ -20,16 +20,13 @@ public: deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner); static TER - deleteSLE(ApplyView& view, std::shared_ptr sle, AccountID const owner, beast::Journal j); + deleteSLE(ApplyView& view, SLE::pointer sle, AccountID const owner, beast::Journal j); TER doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/did/DIDSet.h b/include/xrpl/tx/transactors/did/DIDSet.h index b2c3d97c81..8c84ea6c9b 100644 --- a/include/xrpl/tx/transactors/did/DIDSet.h +++ b/include/xrpl/tx/transactors/did/DIDSet.h @@ -20,10 +20,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/escrow/EscrowCancel.h b/include/xrpl/tx/transactors/escrow/EscrowCancel.h index 92b55374d5..af09f70202 100644 --- a/include/xrpl/tx/transactors/escrow/EscrowCancel.h +++ b/include/xrpl/tx/transactors/escrow/EscrowCancel.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/escrow/EscrowCreate.h b/include/xrpl/tx/transactors/escrow/EscrowCreate.h index 2e9da89896..8800e97b80 100644 --- a/include/xrpl/tx/transactors/escrow/EscrowCreate.h +++ b/include/xrpl/tx/transactors/escrow/EscrowCreate.h @@ -29,10 +29,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/escrow/EscrowFinish.h b/include/xrpl/tx/transactors/escrow/EscrowFinish.h index 806f947b8b..061fa0527c 100644 --- a/include/xrpl/tx/transactors/escrow/EscrowFinish.h +++ b/include/xrpl/tx/transactors/escrow/EscrowFinish.h @@ -32,10 +32,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanBrokerCoverClawback.h b/include/xrpl/tx/transactors/lending/LoanBrokerCoverClawback.h index 1b86ac41e7..81ea97ce7e 100644 --- a/include/xrpl/tx/transactors/lending/LoanBrokerCoverClawback.h +++ b/include/xrpl/tx/transactors/lending/LoanBrokerCoverClawback.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h b/include/xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h index 63e96457dc..43b932726b 100644 --- a/include/xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h +++ b/include/xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.h b/include/xrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.h index e3182c0851..a757ac51bf 100644 --- a/include/xrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.h +++ b/include/xrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanBrokerDelete.h b/include/xrpl/tx/transactors/lending/LoanBrokerDelete.h index 464a43e398..0ce5f29387 100644 --- a/include/xrpl/tx/transactors/lending/LoanBrokerDelete.h +++ b/include/xrpl/tx/transactors/lending/LoanBrokerDelete.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanBrokerSet.h b/include/xrpl/tx/transactors/lending/LoanBrokerSet.h index 72a339951b..75175e2dd6 100644 --- a/include/xrpl/tx/transactors/lending/LoanBrokerSet.h +++ b/include/xrpl/tx/transactors/lending/LoanBrokerSet.h @@ -29,10 +29,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanDelete.h b/include/xrpl/tx/transactors/lending/LoanDelete.h index 8dd9c4601b..9e8c3c172a 100644 --- a/include/xrpl/tx/transactors/lending/LoanDelete.h +++ b/include/xrpl/tx/transactors/lending/LoanDelete.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanManage.h b/include/xrpl/tx/transactors/lending/LoanManage.h index 08c873a5e7..d2344c0ec0 100644 --- a/include/xrpl/tx/transactors/lending/LoanManage.h +++ b/include/xrpl/tx/transactors/lending/LoanManage.h @@ -60,10 +60,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanPay.h b/include/xrpl/tx/transactors/lending/LoanPay.h index 561550e889..9be9695c64 100644 --- a/include/xrpl/tx/transactors/lending/LoanPay.h +++ b/include/xrpl/tx/transactors/lending/LoanPay.h @@ -32,10 +32,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanSet.h b/include/xrpl/tx/transactors/lending/LoanSet.h index 304c077f3d..d277629e44 100644 --- a/include/xrpl/tx/transactors/lending/LoanSet.h +++ b/include/xrpl/tx/transactors/lending/LoanSet.h @@ -39,10 +39,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/nft/NFTokenAcceptOffer.h b/include/xrpl/tx/transactors/nft/NFTokenAcceptOffer.h index 3c98d55141..c5cd10fa6a 100644 --- a/include/xrpl/tx/transactors/nft/NFTokenAcceptOffer.h +++ b/include/xrpl/tx/transactors/nft/NFTokenAcceptOffer.h @@ -11,10 +11,10 @@ private: pay(AccountID const& from, AccountID const& to, STAmount const& amount); TER - acceptOffer(std::shared_ptr const& offer); + acceptOffer(SLE::ref offer); TER - bridgeOffers(std::shared_ptr const& buy, std::shared_ptr const& sell); + bridgeOffers(SLE::ref buy, SLE::ref sell); TER transferNFToken(AccountID const& buyer, AccountID const& seller, uint256 const& nfTokenID); @@ -36,10 +36,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/nft/NFTokenBurn.h b/include/xrpl/tx/transactors/nft/NFTokenBurn.h index 4665a17aa7..849d09cb7e 100644 --- a/include/xrpl/tx/transactors/nft/NFTokenBurn.h +++ b/include/xrpl/tx/transactors/nft/NFTokenBurn.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/nft/NFTokenCancelOffer.h b/include/xrpl/tx/transactors/nft/NFTokenCancelOffer.h index 3eae44b389..a74a1c3e59 100644 --- a/include/xrpl/tx/transactors/nft/NFTokenCancelOffer.h +++ b/include/xrpl/tx/transactors/nft/NFTokenCancelOffer.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/nft/NFTokenCreateOffer.h b/include/xrpl/tx/transactors/nft/NFTokenCreateOffer.h index 20ccefc2cb..c874381dd0 100644 --- a/include/xrpl/tx/transactors/nft/NFTokenCreateOffer.h +++ b/include/xrpl/tx/transactors/nft/NFTokenCreateOffer.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/nft/NFTokenMint.h b/include/xrpl/tx/transactors/nft/NFTokenMint.h index 1a0deae29d..9267e8e801 100644 --- a/include/xrpl/tx/transactors/nft/NFTokenMint.h +++ b/include/xrpl/tx/transactors/nft/NFTokenMint.h @@ -31,10 +31,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/nft/NFTokenModify.h b/include/xrpl/tx/transactors/nft/NFTokenModify.h index 5b197d72b4..0d18e4a6d4 100644 --- a/include/xrpl/tx/transactors/nft/NFTokenModify.h +++ b/include/xrpl/tx/transactors/nft/NFTokenModify.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/oracle/OracleDelete.h b/include/xrpl/tx/transactors/oracle/OracleDelete.h index e83a334b87..c16d5fb2a9 100644 --- a/include/xrpl/tx/transactors/oracle/OracleDelete.h +++ b/include/xrpl/tx/transactors/oracle/OracleDelete.h @@ -32,10 +32,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -46,11 +43,7 @@ public: beast::Journal const& j) override; static TER - deleteOracle( - ApplyView& view, - std::shared_ptr const& sle, - AccountID const& account, - beast::Journal j); + deleteOracle(ApplyView& view, SLE::ref sle, AccountID const& account, beast::Journal j); }; } // namespace xrpl diff --git a/include/xrpl/tx/transactors/oracle/OracleSet.h b/include/xrpl/tx/transactors/oracle/OracleSet.h index 12c022470b..831c11b8c4 100644 --- a/include/xrpl/tx/transactors/oracle/OracleSet.h +++ b/include/xrpl/tx/transactors/oracle/OracleSet.h @@ -32,10 +32,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/payment/DepositPreauth.h b/include/xrpl/tx/transactors/payment/DepositPreauth.h index fbf22f8ca8..742b1ef3f7 100644 --- a/include/xrpl/tx/transactors/payment/DepositPreauth.h +++ b/include/xrpl/tx/transactors/payment/DepositPreauth.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/payment/Payment.h b/include/xrpl/tx/transactors/payment/Payment.h index ef42b67fa4..14897b4efe 100644 --- a/include/xrpl/tx/transactors/payment/Payment.h +++ b/include/xrpl/tx/transactors/payment/Payment.h @@ -41,10 +41,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/payment_channel/PaymentChannelClaim.h b/include/xrpl/tx/transactors/payment_channel/PaymentChannelClaim.h index 98d4638e51..e13fea6d6c 100644 --- a/include/xrpl/tx/transactors/payment_channel/PaymentChannelClaim.h +++ b/include/xrpl/tx/transactors/payment_channel/PaymentChannelClaim.h @@ -29,10 +29,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h b/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h index 73059a7e46..56e984cd57 100644 --- a/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h +++ b/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/payment_channel/PaymentChannelFund.h b/include/xrpl/tx/transactors/payment_channel/PaymentChannelFund.h index 587ee9f778..272076ff20 100644 --- a/include/xrpl/tx/transactors/payment_channel/PaymentChannelFund.h +++ b/include/xrpl/tx/transactors/payment_channel/PaymentChannelFund.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h index 77834f7683..88883fb86f 100644 --- a/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h +++ b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h @@ -24,10 +24,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h index 47c35800dd..4afa8cef5a 100644 --- a/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h +++ b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h @@ -27,10 +27,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/system/Batch.h b/include/xrpl/tx/transactors/system/Batch.h index e190714725..43f0103319 100644 --- a/include/xrpl/tx/transactors/system/Batch.h +++ b/include/xrpl/tx/transactors/system/Batch.h @@ -34,10 +34,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/system/Change.h b/include/xrpl/tx/transactors/system/Change.h index 33df426593..339723ae8e 100644 --- a/include/xrpl/tx/transactors/system/Change.h +++ b/include/xrpl/tx/transactors/system/Change.h @@ -19,10 +19,7 @@ public: preCompute() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/system/LedgerStateFix.h b/include/xrpl/tx/transactors/system/LedgerStateFix.h index 6fbae6fc6a..973f89faa9 100644 --- a/include/xrpl/tx/transactors/system/LedgerStateFix.h +++ b/include/xrpl/tx/transactors/system/LedgerStateFix.h @@ -31,10 +31,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/system/TicketCreate.h b/include/xrpl/tx/transactors/system/TicketCreate.h index 4991d1d08b..5783faa6d1 100644 --- a/include/xrpl/tx/transactors/system/TicketCreate.h +++ b/include/xrpl/tx/transactors/system/TicketCreate.h @@ -61,10 +61,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/token/Clawback.h b/include/xrpl/tx/transactors/token/Clawback.h index 7c99cef0d2..ed90776e59 100644 --- a/include/xrpl/tx/transactors/token/Clawback.h +++ b/include/xrpl/tx/transactors/token/Clawback.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/token/MPTokenAuthorize.h b/include/xrpl/tx/transactors/token/MPTokenAuthorize.h index b2cfa74f6e..e30d123e52 100644 --- a/include/xrpl/tx/transactors/token/MPTokenAuthorize.h +++ b/include/xrpl/tx/transactors/token/MPTokenAuthorize.h @@ -35,10 +35,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h index 6c59d85548..a706c71e18 100644 --- a/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h +++ b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h @@ -51,10 +51,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/token/MPTokenIssuanceDestroy.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceDestroy.h index 65682c8f5e..21032e7337 100644 --- a/include/xrpl/tx/transactors/token/MPTokenIssuanceDestroy.h +++ b/include/xrpl/tx/transactors/token/MPTokenIssuanceDestroy.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/token/MPTokenIssuanceSet.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceSet.h index 7397183bbf..6a6d1fc445 100644 --- a/include/xrpl/tx/transactors/token/MPTokenIssuanceSet.h +++ b/include/xrpl/tx/transactors/token/MPTokenIssuanceSet.h @@ -32,10 +32,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/token/TrustSet.h b/include/xrpl/tx/transactors/token/TrustSet.h index d439b676b5..dcf454bea1 100644 --- a/include/xrpl/tx/transactors/token/TrustSet.h +++ b/include/xrpl/tx/transactors/token/TrustSet.h @@ -30,10 +30,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/vault/VaultClawback.h b/include/xrpl/tx/transactors/vault/VaultClawback.h index 4b9e283571..b8032809ee 100644 --- a/include/xrpl/tx/transactors/vault/VaultClawback.h +++ b/include/xrpl/tx/transactors/vault/VaultClawback.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -39,8 +36,8 @@ public: private: Expected, TER> assetsToClawback( - std::shared_ptr const& vault, - std::shared_ptr const& sleShareIssuance, + SLE::ref vault, + SLE::const_ref sleShareIssuance, AccountID const& holder, STAmount const& clawbackAmount); }; diff --git a/include/xrpl/tx/transactors/vault/VaultCreate.h b/include/xrpl/tx/transactors/vault/VaultCreate.h index bbe80b49c1..9b11f97957 100644 --- a/include/xrpl/tx/transactors/vault/VaultCreate.h +++ b/include/xrpl/tx/transactors/vault/VaultCreate.h @@ -29,10 +29,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/vault/VaultDelete.h b/include/xrpl/tx/transactors/vault/VaultDelete.h index d8fccd6024..b8bb3c4096 100644 --- a/include/xrpl/tx/transactors/vault/VaultDelete.h +++ b/include/xrpl/tx/transactors/vault/VaultDelete.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/vault/VaultDeposit.h b/include/xrpl/tx/transactors/vault/VaultDeposit.h index f1a81c928e..523b3f2e53 100644 --- a/include/xrpl/tx/transactors/vault/VaultDeposit.h +++ b/include/xrpl/tx/transactors/vault/VaultDeposit.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/vault/VaultSet.h b/include/xrpl/tx/transactors/vault/VaultSet.h index 48178f5048..5c362d5db4 100644 --- a/include/xrpl/tx/transactors/vault/VaultSet.h +++ b/include/xrpl/tx/transactors/vault/VaultSet.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/vault/VaultWithdraw.h b/include/xrpl/tx/transactors/vault/VaultWithdraw.h index 7461752ff2..7bbe06187d 100644 --- a/include/xrpl/tx/transactors/vault/VaultWithdraw.h +++ b/include/xrpl/tx/transactors/vault/VaultWithdraw.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/src/libxrpl/ledger/ApplyStateTable.cpp b/src/libxrpl/ledger/ApplyStateTable.cpp index 70fa0aef5d..abcbaac0aa 100644 --- a/src/libxrpl/ledger/ApplyStateTable.cpp +++ b/src/libxrpl/ledger/ApplyStateTable.cpp @@ -80,11 +80,9 @@ ApplyStateTable::size() const void ApplyStateTable::visit( ReadView const& to, - std::function const& before, - std::shared_ptr const& after)> const& func) const + std::function< + void(uint256 const& key, bool isDelete, SLE::const_ref before, SLE::const_ref after)> const& + func) const { for (auto& item : items_) { @@ -339,7 +337,7 @@ ApplyStateTable::succ( return next; } -std::shared_ptr +SLE::const_pointer ApplyStateTable::read(ReadView const& base, Keylet const& k) const { auto const iter = items_.find(k.key); @@ -361,7 +359,7 @@ ApplyStateTable::read(ReadView const& base, Keylet const& k) const return sle; } -std::shared_ptr +SLE::pointer ApplyStateTable::peek(ReadView const& base, Keylet const& k) { auto iter = items_.lower_bound(k.key); @@ -396,7 +394,7 @@ ApplyStateTable::peek(ReadView const& base, Keylet const& k) } void -ApplyStateTable::erase(ReadView const& base, std::shared_ptr const& sle) +ApplyStateTable::erase(ReadView const& base, SLE::ref sle) { auto const iter = items_.find(sle->key()); if (iter == items_.end()) @@ -420,7 +418,7 @@ ApplyStateTable::erase(ReadView const& base, std::shared_ptr const& sle) } void -ApplyStateTable::rawErase(ReadView const& base, std::shared_ptr const& sle) +ApplyStateTable::rawErase(ReadView const& base, SLE::ref sle) { using namespace std; auto const result = items_.emplace( @@ -445,7 +443,7 @@ ApplyStateTable::rawErase(ReadView const& base, std::shared_ptr const& sle) } void -ApplyStateTable::insert(ReadView const& base, std::shared_ptr const& sle) +ApplyStateTable::insert(ReadView const& base, SLE::ref sle) { auto const iter = items_.lower_bound(sle->key()); if (iter == items_.end() || iter->first != sle->key()) @@ -475,7 +473,7 @@ ApplyStateTable::insert(ReadView const& base, std::shared_ptr const& sle) } void -ApplyStateTable::replace(ReadView const& base, std::shared_ptr const& sle) +ApplyStateTable::replace(ReadView const& base, SLE::ref sle) { auto const iter = items_.lower_bound(sle->key()); if (iter == items_.end() || iter->first != sle->key()) @@ -504,7 +502,7 @@ ApplyStateTable::replace(ReadView const& base, std::shared_ptr const& sle) } void -ApplyStateTable::update(ReadView const& base, std::shared_ptr const& sle) +ApplyStateTable::update(ReadView const& base, SLE::ref sle) { auto const iter = items_.find(sle->key()); if (iter == items_.end()) @@ -536,7 +534,7 @@ ApplyStateTable::destroyXRP(XRPAmount const& fee) // Insert this transaction to the SLE's threading list void -ApplyStateTable::threadItem(TxMeta& meta, std::shared_ptr const& sle) +ApplyStateTable::threadItem(TxMeta& meta, SLE::ref sle) { key_type prevTxID; LedgerIndex prevLgrID = 0; @@ -568,7 +566,7 @@ ApplyStateTable::threadItem(TxMeta& meta, std::shared_ptr const& sle) } } -std::shared_ptr +SLE::pointer ApplyStateTable::getForMod(ReadView const& base, key_type const& key, Mods& mods, beast::Journal j) { { @@ -640,7 +638,7 @@ void ApplyStateTable::threadOwners( ReadView const& base, TxMeta& meta, - std::shared_ptr const& sle, + SLE::const_ref sle, Mods& mods, beast::Journal j) { diff --git a/src/libxrpl/ledger/ApplyView.cpp b/src/libxrpl/ledger/ApplyView.cpp index 2200afecac..8575bc2f52 100644 --- a/src/libxrpl/ledger/ApplyView.cpp +++ b/src/libxrpl/ledger/ApplyView.cpp @@ -31,7 +31,7 @@ createRoot( ApplyView& view, Keylet const& directory, uint256 const& key, - std::function const&)> const& describe) + std::function const& describe) { auto newRoot = std::make_shared(directory); newRoot->setFieldH256(sfRootIndex, directory.key); @@ -110,7 +110,7 @@ insertPage( SLE::ref next, uint256 const& key, Keylet const& directory, - std::function const&)> const& describe) + std::function const& describe) { // We rely on modulo arithmetic of unsigned integers (guaranteed in // [basic.fundamental] paragraph 2) to detect page representation overflow. @@ -166,7 +166,7 @@ ApplyView::dirAdd( bool preserveOrder, Keylet const& directory, uint256 const& key, - std::function const&)> const& describe) + std::function const& describe) { auto root = peek(directory); diff --git a/src/libxrpl/ledger/ApplyViewBase.cpp b/src/libxrpl/ledger/ApplyViewBase.cpp index e5a8e11b4c..1b6ba78046 100644 --- a/src/libxrpl/ledger/ApplyViewBase.cpp +++ b/src/libxrpl/ledger/ApplyViewBase.cpp @@ -58,7 +58,7 @@ ApplyViewBase::succ(key_type const& key, std::optional const& last) co return items_.succ(*base_, key, last); } -std::shared_ptr +SLE::const_pointer ApplyViewBase::read(Keylet const& k) const { return items_.read(*base_, k); @@ -114,26 +114,26 @@ ApplyViewBase::flags() const return flags_; } -std::shared_ptr +SLE::pointer ApplyViewBase::peek(Keylet const& k) { return items_.peek(*base_, k); } void -ApplyViewBase::erase(std::shared_ptr const& sle) +ApplyViewBase::erase(SLE::ref sle) { items_.erase(*base_, sle); } void -ApplyViewBase::insert(std::shared_ptr const& sle) +ApplyViewBase::insert(SLE::ref sle) { items_.insert(*base_, sle); } void -ApplyViewBase::update(std::shared_ptr const& sle) +ApplyViewBase::update(SLE::ref sle) { items_.update(*base_, sle); } @@ -141,19 +141,19 @@ ApplyViewBase::update(std::shared_ptr const& sle) //--- void -ApplyViewBase::rawErase(std::shared_ptr const& sle) +ApplyViewBase::rawErase(SLE::ref sle) { items_.rawErase(*base_, sle); } void -ApplyViewBase::rawInsert(std::shared_ptr const& sle) +ApplyViewBase::rawInsert(SLE::ref sle) { items_.insert(*base_, sle); } void -ApplyViewBase::rawReplace(std::shared_ptr const& sle) +ApplyViewBase::rawReplace(SLE::ref sle) { items_.replace(*base_, sle); } diff --git a/src/libxrpl/ledger/ApplyViewImpl.cpp b/src/libxrpl/ledger/ApplyViewImpl.cpp index 9650190a3e..66c009cd88 100644 --- a/src/libxrpl/ledger/ApplyViewImpl.cpp +++ b/src/libxrpl/ledger/ApplyViewImpl.cpp @@ -13,7 +13,6 @@ #include #include -#include #include namespace xrpl { @@ -43,11 +42,9 @@ ApplyViewImpl::size() void ApplyViewImpl::visit( OpenView& to, - std::function const& before, - std::shared_ptr const& after)> const& func) + std::function< + void(uint256 const& key, bool isDelete, SLE::const_ref before, SLE::const_ref after)> const& + func) { items_.visit(to, func); } diff --git a/src/libxrpl/ledger/CachedView.cpp b/src/libxrpl/ledger/CachedView.cpp index 8a6c266b8f..1853cc0ac8 100644 --- a/src/libxrpl/ledger/CachedView.cpp +++ b/src/libxrpl/ledger/CachedView.cpp @@ -7,7 +7,6 @@ #include #include -#include #include #include @@ -19,7 +18,7 @@ CachedViewImpl::exists(Keylet const& k) const return read(k) != nullptr; } -std::shared_ptr +SLE::const_pointer CachedViewImpl::read(Keylet const& k) const { static CountedObjects::Counter kHits{"CachedView::hit"}; diff --git a/src/libxrpl/ledger/Ledger.cpp b/src/libxrpl/ledger/Ledger.cpp index fe7db9a158..82956650e2 100644 --- a/src/libxrpl/ledger/Ledger.cpp +++ b/src/libxrpl/ledger/Ledger.cpp @@ -401,7 +401,7 @@ Ledger::succ(uint256 const& key, std::optional const& last) const return item->key(); } -std::shared_ptr +SLE::const_pointer Ledger::read(Keylet const& k) const { if (k.key == beast::kZero) @@ -486,7 +486,7 @@ Ledger::digest(key_type const& key) const -> std::optional //------------------------------------------------------------------------------ void -Ledger::rawErase(std::shared_ptr const& sle) +Ledger::rawErase(SLE::ref sle) { if (!stateMap_.delItem(sle->key())) logicError("Ledger::rawErase: key not found"); @@ -500,7 +500,7 @@ Ledger::rawErase(uint256 const& key) } void -Ledger::rawInsert(std::shared_ptr const& sle) +Ledger::rawInsert(SLE::ref sle) { Serializer ss; sle->add(ss); @@ -512,7 +512,7 @@ Ledger::rawInsert(std::shared_ptr const& sle) } void -Ledger::rawReplace(std::shared_ptr const& sle) +Ledger::rawReplace(SLE::ref sle) { Serializer ss; sle->add(ss); @@ -623,7 +623,7 @@ Ledger::setup() return ret; } -std::shared_ptr +SLE::pointer Ledger::peek(Keylet const& k) const { auto const& value = stateMap_.peekItem(k.key); diff --git a/src/libxrpl/ledger/OpenView.cpp b/src/libxrpl/ledger/OpenView.cpp index 40b411fcc0..26cb109f85 100644 --- a/src/libxrpl/ledger/OpenView.cpp +++ b/src/libxrpl/ledger/OpenView.cpp @@ -163,7 +163,7 @@ OpenView::succ(key_type const& key, std::optional const& last) const return items_.succ(*base_, key, last); } -std::shared_ptr +SLE::const_pointer OpenView::read(Keylet const& k) const { return items_.read(*base_, k); @@ -228,19 +228,19 @@ OpenView::txRead(key_type const& key) const -> tx_type //--- void -OpenView::rawErase(std::shared_ptr const& sle) +OpenView::rawErase(SLE::ref sle) { items_.erase(sle); } void -OpenView::rawInsert(std::shared_ptr const& sle) +OpenView::rawInsert(SLE::ref sle) { items_.insert(sle); } void -OpenView::rawReplace(std::shared_ptr const& sle) +OpenView::rawReplace(SLE::ref sle) { items_.replace(sle); } diff --git a/src/libxrpl/ledger/RawStateTable.cpp b/src/libxrpl/ledger/RawStateTable.cpp index 69e2f5c0fe..fbe7bf1d82 100644 --- a/src/libxrpl/ledger/RawStateTable.cpp +++ b/src/libxrpl/ledger/RawStateTable.cpp @@ -20,10 +20,10 @@ namespace xrpl::detail { class RawStateTable::SlesIterImpl : public ReadView::SlesType::iter_base { private: - std::shared_ptr sle0_; + SLE::const_pointer sle0_; ReadView::SlesType::Iterator iter0_; ReadView::SlesType::Iterator end0_; - std::shared_ptr sle1_; + SLE::const_pointer sle1_; items_t::const_iterator iter1_; items_t::const_iterator end1_; @@ -241,7 +241,7 @@ RawStateTable::succ(ReadView const& base, key_type const& key, std::optional const& sle) +RawStateTable::erase(SLE::ref sle) { // The base invariant is checked during apply auto const result = items_.emplace( @@ -267,7 +267,7 @@ RawStateTable::erase(std::shared_ptr const& sle) } void -RawStateTable::insert(std::shared_ptr const& sle) +RawStateTable::insert(SLE::ref sle) { auto const result = items_.emplace( std::piecewise_construct, @@ -292,7 +292,7 @@ RawStateTable::insert(std::shared_ptr const& sle) } void -RawStateTable::replace(std::shared_ptr const& sle) +RawStateTable::replace(SLE::ref sle) { auto const result = items_.emplace( std::piecewise_construct, @@ -313,7 +313,7 @@ RawStateTable::replace(std::shared_ptr const& sle) } } -std::shared_ptr +SLE::const_pointer RawStateTable::read(ReadView const& base, Keylet const& k) const { auto const iter = items_.find(k.key); @@ -323,7 +323,7 @@ RawStateTable::read(ReadView const& base, Keylet const& k) const if (item.action == Action::Erase) return nullptr; // Convert to SLE const - std::shared_ptr sle = item.sle; + SLE::const_pointer sle = item.sle; if (!k.check(*sle)) return nullptr; return sle; diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index c62d79dcac..fdd7998609 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -32,7 +32,6 @@ #include #include -#include #include #include @@ -332,11 +331,7 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal) //------------------------------------------------------------------------------ TER -dirLink( - ApplyView& view, - AccountID const& owner, - std::shared_ptr& object, - SF_UINT64 const& node) +dirLink(ApplyView& view, AccountID const& owner, SLE::pointer& object, SF_UINT64 const& node) { auto const page = view.dirInsert(keylet::ownerDir(owner), object->key(), describeOwnerDir(owner)); @@ -488,7 +483,7 @@ cleanupOnAccountDelete( std::optional maxNodesToDelete) { // Delete all the entries in the account directory. - std::shared_ptr sleDirNode{}; + SLE::pointer sleDirNode{}; unsigned int uDirEntry{0}; uint256 dirEntry{beast::kZero}; std::uint32_t deleted = 0; diff --git a/src/libxrpl/ledger/helpers/AMMHelpers.cpp b/src/libxrpl/ledger/helpers/AMMHelpers.cpp index f7aabc8ea5..fe6d022490 100644 --- a/src/libxrpl/ledger/helpers/AMMHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AMMHelpers.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -636,7 +635,7 @@ deleteAMMTrustLines( keylet::ownerDir(ammAccountID), [&](LedgerEntryType nodeType, uint256 const&, - std::shared_ptr& sleItem) -> std::pair { + SLE::pointer& sleItem) -> std::pair { // Skip AMM and MPToken if (nodeType == ltAMM || nodeType == ltMPTOKEN) return {tesSUCCESS, SkipEntry::Yes}; @@ -672,7 +671,7 @@ deleteAMMMPTokens(Sandbox& sb, AccountID const& ammAccountID, beast::Journal j) keylet::ownerDir(ammAccountID), [&](LedgerEntryType nodeType, uint256 const&, - std::shared_ptr& sleItem) -> std::pair { + SLE::pointer& sleItem) -> std::pair { // Skip AMM if (nodeType == ltAMM) return {tesSUCCESS, SkipEntry::Yes}; @@ -768,7 +767,7 @@ deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Jo void initializeFeeAuctionVote( ApplyView& view, - std::shared_ptr& ammSle, + SLE::pointer& ammSle, AccountID const& account, Asset const& lptAsset, std::uint16_t tfee) @@ -926,7 +925,7 @@ Expected verifyAndAdjustLPTokenBalance( Sandbox& sb, STAmount const& lpTokens, - std::shared_ptr& ammSle, + SLE::pointer& ammSle, AccountID const& account) { auto const res = isOnlyLiquidityProvider(sb, lpTokens.get(), account); diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index 029cb5cd92..1634de93c9 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -125,11 +125,7 @@ transferRate(ReadView const& view, AccountID const& issuer) } void -adjustOwnerCount( - ApplyView& view, - std::shared_ptr const& sle, - std::int32_t amount, - beast::Journal j) +adjustOwnerCount(ApplyView& view, SLE::ref sle, std::int32_t amount, beast::Journal j) { if (!sle) return; @@ -192,9 +188,7 @@ getPseudoAccountFields() } [[nodiscard]] bool -isPseudoAccount( - std::shared_ptr sleAcct, - std::set const& pseudoFieldFilter) +isPseudoAccount(SLE::const_pointer sleAcct, std::set const& pseudoFieldFilter) { auto const& fields = getPseudoAccountFields(); @@ -208,7 +202,7 @@ isPseudoAccount( }) > 0; } -Expected, TER> +Expected createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField) { [[maybe_unused]] diff --git a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp index 838e72d364..28b50b51d6 100644 --- a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp +++ b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp @@ -25,7 +25,6 @@ #include #include -#include #include #include #include @@ -71,7 +70,7 @@ removeExpired(ApplyView& view, STVector256 const& arr, beast::Journal const j) } TER -deleteSLE(ApplyView& view, std::shared_ptr const& sleCredential, beast::Journal j) +deleteSLE(ApplyView& view, SLE::ref sleCredential, beast::Journal j) { if (!sleCredential) return tecNO_ENTRY; @@ -231,7 +230,7 @@ TER authorizedDepositPreauth(ReadView const& view, STVector256 const& credIDs, AccountID const& dst) { std::set> sorted; - std::vector> lifeExtender; + std::vector lifeExtender; lifeExtender.reserve(credIDs.size()); for (auto const& h : credIDs) { @@ -352,7 +351,7 @@ verifyDepositPreauth( ApplyView& view, AccountID const& src, AccountID const& dst, - std::shared_ptr const& sleDst, + SLE::const_ref sleDst, beast::Journal j) { // If depositPreauth is enabled, then an account that requires diff --git a/src/libxrpl/ledger/helpers/DirectoryHelpers.cpp b/src/libxrpl/ledger/helpers/DirectoryHelpers.cpp index 8b4eeae7b7..43f8219b6a 100644 --- a/src/libxrpl/ledger/helpers/DirectoryHelpers.cpp +++ b/src/libxrpl/ledger/helpers/DirectoryHelpers.cpp @@ -13,15 +13,13 @@ #include #include -#include - namespace xrpl { bool dirFirst( ApplyView& view, uint256 const& root, - std::shared_ptr& page, + SLE::pointer& page, unsigned int& index, uint256& entry) { @@ -32,7 +30,7 @@ bool dirNext( ApplyView& view, uint256 const& root, - std::shared_ptr& page, + SLE::pointer& page, unsigned int& index, uint256& entry) { @@ -43,7 +41,7 @@ bool cdirFirst( ReadView const& view, uint256 const& root, - std::shared_ptr& page, + SLE::const_pointer& page, unsigned int& index, uint256& entry) { @@ -54,7 +52,7 @@ bool cdirNext( ReadView const& view, uint256 const& root, - std::shared_ptr& page, + SLE::const_pointer& page, unsigned int& index, uint256& entry) { @@ -62,10 +60,7 @@ cdirNext( } void -forEachItem( - ReadView const& view, - Keylet const& root, - std::function const&)> const& f) +forEachItem(ReadView const& view, Keylet const& root, std::function const& f) { XRPL_ASSERT(root.type == ltDIR_NODE, "xrpl::forEachItem : valid root type"); @@ -95,7 +90,7 @@ forEachItemAfter( uint256 const& after, std::uint64_t const hint, unsigned int limit, - std::function const&)> const& f) + std::function const& f) { XRPL_ASSERT(root.type == ltDIR_NODE, "xrpl::forEachItemAfter : valid root type"); @@ -184,7 +179,7 @@ dirIsEmpty(ReadView const& view, Keylet const& k) std::function describeOwnerDir(AccountID const& account) { - return [account](std::shared_ptr const& sle) { (*sle)[sfOwner] = account; }; + return [account](SLE::ref sle) { (*sle)[sfOwner] = account; }; } } // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index bb784278ba..5b467db796 100644 --- a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -41,7 +41,7 @@ namespace xrpl::nft { -static std::shared_ptr +static SLE::const_pointer locatePage(ReadView const& view, AccountID const& owner, uint256 const& id) { auto const first = keylet::nftpage(keylet::nftpageMin(owner), id); @@ -54,7 +54,7 @@ locatePage(ReadView const& view, AccountID const& owner, uint256 const& id) Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key))); } -static std::shared_ptr +static SLE::pointer locatePage(ApplyView& view, AccountID const& owner, uint256 const& id) { auto const first = keylet::nftpage(keylet::nftpageMin(owner), id); @@ -67,7 +67,7 @@ locatePage(ApplyView& view, AccountID const& owner, uint256 const& id) Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key))); } -static std::shared_ptr +static SLE::pointer getPageForToken( ApplyView& view, AccountID const& owner, @@ -230,7 +230,7 @@ changeTokenURI( uint256 const& nftokenID, std::optional const& uri) { - std::shared_ptr const page = locatePage(view, owner, nftokenID); + SLE::pointer const page = locatePage(view, owner, nftokenID); // If the page couldn't be found, the given NFT isn't owned by this account if (!page) @@ -267,7 +267,7 @@ insertToken(ApplyView& view, AccountID owner, STObject&& nft) // First, we need to locate the page the NFT belongs to, creating it // if necessary. This operation may fail if it is impossible to insert // the NFT. - std::shared_ptr const page = + SLE::pointer const page = getPageForToken(view, owner, nft[sfNFTokenID], [](ApplyView& view, AccountID const& owner) { adjustOwnerCount( view, @@ -296,7 +296,7 @@ insertToken(ApplyView& view, AccountID owner, STObject&& nft) } static bool -mergePages(ApplyView& view, std::shared_ptr const& p1, std::shared_ptr const& p2) +mergePages(ApplyView& view, SLE::ref p1, SLE::ref p2) { if (p1->key() >= p2->key()) Throw("mergePages: pages passed in out of order!"); @@ -355,7 +355,7 @@ mergePages(ApplyView& view, std::shared_ptr const& p1, std::shared_ptr TER removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID) { - std::shared_ptr const page = locatePage(view, owner, nftokenID); + SLE::pointer const page = locatePage(view, owner, nftokenID); // If the page couldn't be found, the given NFT isn't owned by this account if (!page) @@ -366,11 +366,7 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID) /** Remove the token from the owner's token directory. */ TER -removeToken( - ApplyView& view, - AccountID const& owner, - uint256 const& nftokenID, - std::shared_ptr const& curr) +removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, SLE::ref curr) { // We found a page, but the given NFT may not be in it. auto arr = curr->getFieldArray(sfNFTokens); @@ -386,8 +382,8 @@ removeToken( } // Page management: - auto const loadPage = [&view](std::shared_ptr const& page1, SF_UINT256 const& field) { - std::shared_ptr page2; + auto const loadPage = [&view](SLE::ref page1, SF_UINT256 const& field) { + SLE::pointer page2; if (auto const id = (*page1)[~field]) { @@ -535,7 +531,7 @@ removeToken( std::optional findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID) { - std::shared_ptr const page = locatePage(view, owner, nftokenID); + SLE::const_pointer const page = locatePage(view, owner, nftokenID); // If the page couldn't be found, the given NFT isn't owned by this account if (!page) @@ -554,7 +550,7 @@ findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID std::optional findTokenAndPage(ApplyView& view, AccountID const& owner, uint256 const& nftokenID) { - std::shared_ptr page = locatePage(view, owner, nftokenID); + SLE::pointer page = locatePage(view, owner, nftokenID); // If the page couldn't be found, the given NFT isn't owned by this account if (!page) @@ -623,7 +619,7 @@ removeTokenOffersWithLimit(ApplyView& view, Keylet const& directory, std::size_t } bool -deleteTokenOffer(ApplyView& view, std::shared_ptr const& offer) +deleteTokenOffer(ApplyView& view, SLE::ref offer) { if (offer->getType() != ltNFTOKEN_OFFER) return false; @@ -657,7 +653,7 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner) auto const last = keylet::nftpageMax(owner); - std::shared_ptr page = view.peek(Keylet( + SLE::pointer page = view.peek(Keylet( ltNFTOKEN_PAGE, view.succ(keylet::nftpageMin(owner).key, last.key.next()).value_or(last.key))); @@ -691,7 +687,7 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner) view.update(page); } - std::shared_ptr nextPage; + SLE::pointer nextPage; while ( (nextPage = view.peek(Keylet( ltNFTOKEN_PAGE, view.succ(page->key().next(), last.key.next()).value_or(last.key))))) @@ -956,7 +952,7 @@ tokenOfferCreateApply( auto const offerNode = view.dirInsert( isSellOffer ? keylet::nftSells(nftokenID) : keylet::nftBuys(nftokenID), offerID, - [&nftokenID, isSellOffer](std::shared_ptr const& sle) { + [&nftokenID, isSellOffer](SLE::ref sle) { (*sle)[sfFlags] = isSellOffer ? lsfNFTokenSellOffers : lsfNFTokenBuyOffers; (*sle)[sfNFTokenID] = nftokenID; }); diff --git a/src/libxrpl/ledger/helpers/OfferHelpers.cpp b/src/libxrpl/ledger/helpers/OfferHelpers.cpp index 03a1170aad..5249870143 100644 --- a/src/libxrpl/ledger/helpers/OfferHelpers.cpp +++ b/src/libxrpl/ledger/helpers/OfferHelpers.cpp @@ -12,12 +12,10 @@ #include #include -#include - namespace xrpl { TER -offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j) +offerDelete(ApplyView& view, SLE::ref sle, beast::Journal j) { if (!sle) return tesSUCCESS; diff --git a/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp b/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp index 31c206d85b..e755dbaca3 100644 --- a/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp +++ b/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp @@ -12,16 +12,10 @@ #include #include -#include - namespace xrpl { TER -closeChannel( - std::shared_ptr const& slep, - ApplyView& view, - uint256 const& key, - beast::Journal j) +closeChannel(SLE::ref slep, ApplyView& view, uint256 const& key, beast::Journal j) { AccountID const src = (*slep)[sfAccount]; // Remove PayChan from owner directory diff --git a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp index 5aaa417ad9..7ed7ab8fc4 100644 --- a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp +++ b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp @@ -294,7 +294,7 @@ trustCreate( TER trustDelete( ApplyView& view, - std::shared_ptr const& sleRippleState, + SLE::ref sleRippleState, AccountID const& uLowAccountID, AccountID const& uHighAccountID, beast::Journal j) @@ -738,7 +738,7 @@ removeEmptyHolding( TER deleteAMMTrustLine( ApplyView& view, - std::shared_ptr sleState, + SLE::pointer sleState, std::optional const& ammAccountID, beast::Journal j) { @@ -786,7 +786,7 @@ deleteAMMTrustLine( TER deleteAMMMPToken( ApplyView& view, - std::shared_ptr sleMpt, + SLE::pointer sleMpt, AccountID const& ammAccountID, beast::Journal j) { diff --git a/src/libxrpl/ledger/helpers/VaultHelpers.cpp b/src/libxrpl/ledger/helpers/VaultHelpers.cpp index 587923953d..3a3a756499 100644 --- a/src/libxrpl/ledger/helpers/VaultHelpers.cpp +++ b/src/libxrpl/ledger/helpers/VaultHelpers.cpp @@ -12,16 +12,12 @@ #include // IWYU pragma: keep #include -#include #include namespace xrpl { [[nodiscard]] std::optional -assetsToSharesDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& assets) +assetsToSharesDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& assets) { XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesDeposit : non-negative assets"); XRPL_ASSERT( @@ -45,10 +41,7 @@ assetsToSharesDeposit( } [[nodiscard]] std::optional -sharesToAssetsDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& shares) +sharesToAssetsDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& shares) { XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsDeposit : non-negative shares"); XRPL_ASSERT( @@ -72,8 +65,8 @@ sharesToAssetsDeposit( [[nodiscard]] std::optional assetsToSharesWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, + SLE::const_ref vault, + SLE::const_ref issuance, STAmount const& assets, TruncateShares truncate, WaiveUnrealizedLoss waive) @@ -101,8 +94,8 @@ assetsToSharesWithdraw( [[nodiscard]] std::optional sharesToAssetsWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, + SLE::const_ref vault, + SLE::const_ref issuance, STAmount const& shares, WaiveUnrealizedLoss waive) { diff --git a/src/libxrpl/tx/ApplyContext.cpp b/src/libxrpl/tx/ApplyContext.cpp index 88e37baf00..93b0d101af 100644 --- a/src/libxrpl/tx/ApplyContext.cpp +++ b/src/libxrpl/tx/ApplyContext.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -70,11 +69,7 @@ ApplyContext::size() void ApplyContext::visit( - std::function const&, - std::shared_ptr const&)> const& func) + std::function const& func) { view_->visit(base_, func); // NOLINT(bugprone-unchecked-optional-access) } @@ -104,13 +99,11 @@ ApplyContext::checkInvariantsHelper( auto checkers = getInvariantChecks(); // call each check's per-entry method - visit([&checkers]( - uint256 const& index, - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) { - (..., std::get(checkers).visitEntry(isDelete, before, after)); - }); + visit( + [&checkers]( + uint256 const& index, bool isDelete, SLE::const_ref before, SLE::const_ref after) { + (..., std::get(checkers).visitEntry(isDelete, before, after)); + }); // Note: do not replace this logic with a `...&&` fold expression. // The fold expression will only run until the first check fails (it diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 28fa059902..51541cc2e3 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -789,7 +788,7 @@ Transactor::checkSingleSign( ReadView const& view, AccountID const& idSigner, AccountID const& idAccount, - std::shared_ptr sleAccount, + SLE::const_pointer sleAccount, beast::Journal const j) { bool const isMasterDisabled = sleAccount->isFlag(lsfDisableMaster); @@ -825,7 +824,7 @@ Transactor::checkMultiSign( beast::Journal const j) { // Get id's SignerList and Quorum. - std::shared_ptr const sleAccountSigners = view.read(keylet::signers(id)); + STLedgerEntry::const_pointer const sleAccountSigners = view.read(keylet::signers(id)); // If the signer list doesn't exist the account is not multi-signing. if (!sleAccountSigners) { @@ -1286,8 +1285,8 @@ Transactor::operator()() &expiredCredentials]( uint256 const& index, bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) { + SLE::const_ref before, + SLE::const_ref after) { if (isDelete) { XRPL_ASSERT( diff --git a/src/libxrpl/tx/invariants/AMMInvariant.cpp b/src/libxrpl/tx/invariants/AMMInvariant.cpp index be2a803e93..ecd7bedf89 100644 --- a/src/libxrpl/tx/invariants/AMMInvariant.cpp +++ b/src/libxrpl/tx/invariants/AMMInvariant.cpp @@ -19,16 +19,12 @@ #include #include -#include #include namespace xrpl { void -ValidAMM::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidAMM::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { if (isDelete) return; diff --git a/src/libxrpl/tx/invariants/FreezeInvariant.cpp b/src/libxrpl/tx/invariants/FreezeInvariant.cpp index eaaeb7fc6f..0293d42e97 100644 --- a/src/libxrpl/tx/invariants/FreezeInvariant.cpp +++ b/src/libxrpl/tx/invariants/FreezeInvariant.cpp @@ -16,16 +16,12 @@ #include #include -#include #include namespace xrpl { void -TransfersNotFrozen::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +TransfersNotFrozen::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { /* * A trust line freeze state alone doesn't determine if a transfer is @@ -107,9 +103,7 @@ TransfersNotFrozen::finalize( } bool -TransfersNotFrozen::isValidEntry( - std::shared_ptr const& before, - std::shared_ptr const& after) +TransfersNotFrozen::isValidEntry(SLE::const_ref before, SLE::const_ref after) { // `after` can never be null, even if the trust line is deleted. XRPL_ASSERT(after, "xrpl::TransfersNotFrozen::isValidEntry : valid after."); @@ -135,8 +129,8 @@ TransfersNotFrozen::isValidEntry( STAmount TransfersNotFrozen::calculateBalanceChange( - std::shared_ptr const& before, - std::shared_ptr const& after, + SLE::const_ref before, + SLE::const_ref after, bool isDelete) { auto const getBalance = [](auto const& line, auto const& other, bool zero) { @@ -180,9 +174,7 @@ TransfersNotFrozen::recordBalance(Issue const& issue, BalanceChange change) } void -TransfersNotFrozen::recordBalanceChanges( - std::shared_ptr const& after, - STAmount const& balanceChange) +TransfersNotFrozen::recordBalanceChanges(SLE::const_ref after, STAmount const& balanceChange) { auto const balanceChangeSign = balanceChange.signum(); auto const currency = after->at(sfBalance).get().currency; @@ -198,7 +190,7 @@ TransfersNotFrozen::recordBalanceChanges( {.line = after, .balanceChangeSign = -balanceChangeSign}); } -std::shared_ptr +SLE::const_pointer TransfersNotFrozen::findIssuer(AccountID const& issuerID, ReadView const& view) { if (auto it = possibleIssuers_.find(issuerID); it != possibleIssuers_.end()) @@ -211,7 +203,7 @@ TransfersNotFrozen::findIssuer(AccountID const& issuerID, ReadView const& view) bool TransfersNotFrozen::validateIssuerChanges( - std::shared_ptr const& issuer, + SLE::const_ref issuer, IssuerChanges const& changes, STTx const& tx, beast::Journal const& j, diff --git a/src/libxrpl/tx/invariants/InvariantCheck.cpp b/src/libxrpl/tx/invariants/InvariantCheck.cpp index 0154dca747..b4a533905c 100644 --- a/src/libxrpl/tx/invariants/InvariantCheck.cpp +++ b/src/libxrpl/tx/invariants/InvariantCheck.cpp @@ -64,10 +64,7 @@ hasPrivilege(STTx const& tx, Privilege priv) #pragma pop_macro("TRANSACTION") void -TransactionFeeCheck::visitEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +TransactionFeeCheck::visitEntry(bool, SLE::const_ref, SLE::const_ref) { // nothing to do } @@ -110,10 +107,7 @@ TransactionFeeCheck::finalize( //------------------------------------------------------------------------------ void -XRPNotCreated::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +XRPNotCreated::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { /* We go through all modified ledger entries, looking only at account roots, * escrow payments, and payment channels. We remove from the total any @@ -192,10 +186,7 @@ XRPNotCreated::finalize( //------------------------------------------------------------------------------ void -XRPBalanceChecks::visitEntry( - bool, - std::shared_ptr const& before, - std::shared_ptr const& after) +XRPBalanceChecks::visitEntry(bool, SLE::const_ref before, SLE::const_ref after) { auto isBad = [](STAmount const& balance) { if (!balance.native()) @@ -242,10 +233,7 @@ XRPBalanceChecks::finalize( //------------------------------------------------------------------------------ void -NoBadOffers::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +NoBadOffers::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { auto isBad = [](STAmount const& pays, STAmount const& gets) { // An offer should never be negative @@ -286,10 +274,7 @@ NoBadOffers::finalize( //------------------------------------------------------------------------------ void -NoZeroEscrow::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +NoZeroEscrow::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { auto isBad = [](STAmount const& amount) { // XRP case @@ -393,10 +378,7 @@ NoZeroEscrow::finalize( //------------------------------------------------------------------------------ void -AccountRootsNotDeleted::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const&) +AccountRootsNotDeleted::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref) { if (isDelete && before && before->getType() == ltACCOUNT_ROOT) accountsDeleted_++; @@ -446,10 +428,7 @@ AccountRootsNotDeleted::finalize( //------------------------------------------------------------------------------ void -AccountRootsDeletedClean::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +AccountRootsDeletedClean::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { if (isDelete && before && before->getType() == ltACCOUNT_ROOT) accountsDeleted_.emplace_back(before, after); @@ -566,10 +545,7 @@ AccountRootsDeletedClean::finalize( //------------------------------------------------------------------------------ void -LedgerEntryTypesMatch::visitEntry( - bool, - std::shared_ptr const& before, - std::shared_ptr const& after) +LedgerEntryTypesMatch::visitEntry(bool, SLE::const_ref before, SLE::const_ref after) { if (before && after && before->getType() != after->getType()) typeMismatch_ = true; @@ -623,10 +599,7 @@ LedgerEntryTypesMatch::finalize( //------------------------------------------------------------------------------ void -NoXRPTrustLines::visitEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const& after) +NoXRPTrustLines::visitEntry(bool, SLE::const_ref, SLE::const_ref after) { bool const overwriteFixEnabled = isFeatureEnabled(fixCleanup3_1_3, true); @@ -666,10 +639,7 @@ NoXRPTrustLines::finalize( //------------------------------------------------------------------------------ void -NoDeepFreezeTrustLinesWithoutFreeze::visitEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const& after) +NoDeepFreezeTrustLinesWithoutFreeze::visitEntry(bool, SLE::const_ref, SLE::const_ref after) { if (after && after->getType() == ltRIPPLE_STATE) { @@ -712,10 +682,7 @@ NoDeepFreezeTrustLinesWithoutFreeze::finalize( //------------------------------------------------------------------------------ void -ValidNewAccountRoot::visitEntry( - bool, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidNewAccountRoot::visitEntry(bool, SLE::const_ref before, SLE::const_ref after) { if (!before && after->getType() == ltACCOUNT_ROOT) { @@ -789,10 +756,7 @@ ValidNewAccountRoot::finalize( //------------------------------------------------------------------------------ void -ValidClawback::visitEntry( - bool, - std::shared_ptr const& before, - std::shared_ptr const&) +ValidClawback::visitEntry(bool, SLE::const_ref before, SLE::const_ref) { if (before && before->getType() == ltRIPPLE_STATE) trustlinesChanged_++; @@ -877,10 +841,7 @@ ValidClawback::finalize( //------------------------------------------------------------------------------ void -ValidPseudoAccounts::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidPseudoAccounts::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { if (isDelete) { @@ -968,10 +929,7 @@ ValidPseudoAccounts::finalize( //------------------------------------------------------------------------------ void -NoModifiedUnmodifiableFields::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +NoModifiedUnmodifiableFields::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { if (isDelete || !before) { diff --git a/src/libxrpl/tx/invariants/LoanBrokerInvariant.cpp b/src/libxrpl/tx/invariants/LoanBrokerInvariant.cpp index 56995ef94c..8586a27be3 100644 --- a/src/libxrpl/tx/invariants/LoanBrokerInvariant.cpp +++ b/src/libxrpl/tx/invariants/LoanBrokerInvariant.cpp @@ -15,15 +15,10 @@ #include #include -#include - namespace xrpl { void -ValidLoanBroker::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidLoanBroker::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { if (after) { diff --git a/src/libxrpl/tx/invariants/LoanInvariant.cpp b/src/libxrpl/tx/invariants/LoanInvariant.cpp index 3eef0957e5..ce9a7c6e03 100644 --- a/src/libxrpl/tx/invariants/LoanInvariant.cpp +++ b/src/libxrpl/tx/invariants/LoanInvariant.cpp @@ -12,15 +12,10 @@ #include #include -#include - namespace xrpl { void -ValidLoan::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidLoan::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { if (after && after->getType() == ltLOAN) { diff --git a/src/libxrpl/tx/invariants/MPTInvariant.cpp b/src/libxrpl/tx/invariants/MPTInvariant.cpp index 635af25b61..a2369cc1b9 100644 --- a/src/libxrpl/tx/invariants/MPTInvariant.cpp +++ b/src/libxrpl/tx/invariants/MPTInvariant.cpp @@ -24,14 +24,10 @@ #include #include #include - namespace xrpl { void -ValidMPTIssuance::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidMPTIssuance::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { // The sfReferenceHolding tracking and the deleted-holding capture are // only meaningful post-fixCleanup3_2_0 (the field is never set @@ -369,10 +365,7 @@ ValidMPTIssuance::finalize( } void -ValidMPTPayment::visitEntry( - bool, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidMPTPayment::visitEntry(bool, SLE::const_ref before, SLE::const_ref after) { if (overflow_) return; diff --git a/src/libxrpl/tx/invariants/NFTInvariant.cpp b/src/libxrpl/tx/invariants/NFTInvariant.cpp index 09ae9db925..52ecbcd9d1 100644 --- a/src/libxrpl/tx/invariants/NFTInvariant.cpp +++ b/src/libxrpl/tx/invariants/NFTInvariant.cpp @@ -20,16 +20,12 @@ #include #include -#include #include namespace xrpl { void -ValidNFTokenPage::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidNFTokenPage::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { static constexpr uint256 const& kPageBits = nft::kPageMask; static constexpr uint256 kAccountBits = ~kPageBits; @@ -38,7 +34,7 @@ ValidNFTokenPage::visitEntry( (after && after->getType() != ltNFTOKEN_PAGE)) return; - auto check = [this, isDelete](std::shared_ptr const& sle) { + auto check = [this, isDelete](SLE::const_ref sle) { uint256 const account = sle->key() & kAccountBits; uint256 const hiLimit = sle->key() & kPageBits; std::optional const prev = (*sle)[~sfPreviousPageMin]; @@ -187,10 +183,7 @@ ValidNFTokenPage::finalize( //------------------------------------------------------------------------------ void -NFTokenCountTracking::visitEntry( - bool, - std::shared_ptr const& before, - std::shared_ptr const& after) +NFTokenCountTracking::visitEntry(bool, SLE::const_ref before, SLE::const_ref after) { if (before && before->getType() == ltACCOUNT_ROOT) { diff --git a/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp b/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp index 282df85302..1014642b36 100644 --- a/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp +++ b/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp @@ -14,15 +14,10 @@ #include #include -#include - namespace xrpl { void -ValidPermissionedDEX::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidPermissionedDEX::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { if (after && after->getType() == ltDIR_NODE) { diff --git a/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp b/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp index 93c1b2058e..544a3af2dc 100644 --- a/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp +++ b/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp @@ -15,23 +15,19 @@ #include #include -#include #include namespace xrpl { void -ValidPermissionedDomain::visitEntry( - bool isDel, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidPermissionedDomain::visitEntry(bool isDel, SLE::const_ref before, SLE::const_ref after) { if (before && before->getType() != ltPERMISSIONED_DOMAIN) return; if (after && after->getType() != ltPERMISSIONED_DOMAIN) return; - auto check = [isDel](std::vector& sleStatus, std::shared_ptr const& sle) { + auto check = [isDel](std::vector& sleStatus, SLE::const_ref sle) { auto const& credentials = sle->getFieldArray(sfAcceptedCredentials); auto const sorted = credentials::makeSorted(credentials); diff --git a/src/libxrpl/tx/invariants/VaultInvariant.cpp b/src/libxrpl/tx/invariants/VaultInvariant.cpp index 4aa79279a1..80b8f36bd9 100644 --- a/src/libxrpl/tx/invariants/VaultInvariant.cpp +++ b/src/libxrpl/tx/invariants/VaultInvariant.cpp @@ -23,7 +23,6 @@ #include #include -#include #include #include #include @@ -63,10 +62,7 @@ ValidVault::Shares::make(SLE const& from) } void -ValidVault::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidVault::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { // If `before` is empty, this means an object is being created, in which // case `isDelete` must be false. Otherwise `before` and `after` are set and diff --git a/src/libxrpl/tx/paths/BookTip.cpp b/src/libxrpl/tx/paths/BookTip.cpp index 5be512aa84..eda11e5d72 100644 --- a/src/libxrpl/tx/paths/BookTip.cpp +++ b/src/libxrpl/tx/paths/BookTip.cpp @@ -8,8 +8,6 @@ #include #include -#include - namespace xrpl { BookTip::BookTip(ApplyView& view, Book const& book) @@ -40,7 +38,7 @@ BookTip::step(beast::Journal j) return false; unsigned int di = 0; - std::shared_ptr dir; + SLE::pointer dir; if (dirFirst(view_, *firstPage, dir, di, index_)) { diff --git a/src/libxrpl/tx/paths/DirectStep.cpp b/src/libxrpl/tx/paths/DirectStep.cpp index a16c3afd6a..cc6e75ea3d 100644 --- a/src/libxrpl/tx/paths/DirectStep.cpp +++ b/src/libxrpl/tx/paths/DirectStep.cpp @@ -269,7 +269,7 @@ public: // Verify the consistency of the step. These checks are specific to // payments and assume that general checks were already performed. [[nodiscard]] TER - check(StrandContext const& ctx, std::shared_ptr const& sleSrc) const; + check(StrandContext const& ctx, SLE::const_ref sleSrc) const; [[nodiscard]] std::string logString() const override @@ -327,7 +327,7 @@ public: // Verify the consistency of the step. These checks are specific to // offer crossing and assume that general checks were already performed. static TER - check(StrandContext const& ctx, std::shared_ptr const& sleSrc); + check(StrandContext const& ctx, SLE::const_ref sleSrc); [[nodiscard]] std::string logString() const override @@ -415,7 +415,7 @@ DirectIOfferCrossingStep::maxFlow(ReadView const& sb, IOUAmount const& desired) } TER -DirectIPaymentStep::check(StrandContext const& ctx, std::shared_ptr const& sleSrc) const +DirectIPaymentStep::check(StrandContext const& ctx, SLE::const_ref sleSrc) const { // Since this is a payment a trust line must be present. Perform all // trust line related checks. @@ -463,7 +463,7 @@ DirectIPaymentStep::check(StrandContext const& ctx, std::shared_ptr c } TER -DirectIOfferCrossingStep::check(StrandContext const&, std::shared_ptr const&) +DirectIOfferCrossingStep::check(StrandContext const&, SLE::const_ref) { // The standard checks are all we can do because any remaining checks // require the existence of a trust line. Offer crossing does not diff --git a/src/libxrpl/tx/paths/MPTEndpointStep.cpp b/src/libxrpl/tx/paths/MPTEndpointStep.cpp index 5595f8ea65..7dcd6d9241 100644 --- a/src/libxrpl/tx/paths/MPTEndpointStep.cpp +++ b/src/libxrpl/tx/paths/MPTEndpointStep.cpp @@ -264,7 +264,7 @@ public: // Verify the consistency of the step. These checks are specific to // payments and assume that general checks were already performed. [[nodiscard]] TER - check(StrandContext const& ctx, std::shared_ptr const& sleSrc) const; + check(StrandContext const& ctx, SLE::const_ref sleSrc) const; [[nodiscard]] std::string logString() const override @@ -312,7 +312,7 @@ public: // Verify the consistency of the step. These checks are specific to // offer crossing and assume that general checks were already performed. static TER - check(StrandContext const& ctx, std::shared_ptr const& sleSrc); + check(StrandContext const& ctx, SLE::const_ref sleSrc); [[nodiscard]] std::string logString() const override @@ -328,8 +328,7 @@ public: //------------------------------------------------------------------------------ TER -MPTEndpointPaymentStep::check(StrandContext const& ctx, std::shared_ptr const& sleSrc) - const +MPTEndpointPaymentStep::check(StrandContext const& ctx, SLE::const_ref sleSrc) const { // Since this is a payment, MPToken must be present. Perform all // MPToken related checks. @@ -393,7 +392,7 @@ MPTEndpointPaymentStep::check(StrandContext const& ctx, std::shared_ptr const&) +MPTEndpointOfferCrossingStep::check(StrandContext const& ctx, SLE::const_ref) { // The standard checks are all we can do because any remaining checks // require the existence of a MPToken. Offer crossing does not diff --git a/src/libxrpl/tx/paths/OfferStream.cpp b/src/libxrpl/tx/paths/OfferStream.cpp index bfe2bbd1bb..b7defb4df8 100644 --- a/src/libxrpl/tx/paths/OfferStream.cpp +++ b/src/libxrpl/tx/paths/OfferStream.cpp @@ -27,7 +27,6 @@ #include #include -#include #include namespace xrpl { @@ -205,7 +204,7 @@ TOfferStreamBase::step() if (!tip_.step(j_)) return false; - std::shared_ptr const entry = tip_.entry(); + SLE::pointer const entry = tip_.entry(); // If we exceed the maximum number of allowed steps, we're done. if (!counter_.step()) diff --git a/src/libxrpl/tx/transactors/account/AccountDelete.cpp b/src/libxrpl/tx/transactors/account/AccountDelete.cpp index c0e8fe05c6..833f1c8b25 100644 --- a/src/libxrpl/tx/transactors/account/AccountDelete.cpp +++ b/src/libxrpl/tx/transactors/account/AccountDelete.cpp @@ -32,7 +32,6 @@ #include #include -#include #include namespace xrpl { @@ -72,7 +71,7 @@ using DeleterFuncPtr = TER (*)( ApplyView& view, AccountID const& account, uint256 const& delIndex, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal j); // Local function definitions that provides signature compatibility. @@ -82,7 +81,7 @@ offerDelete( ApplyView& view, AccountID const& account, uint256 const& delIndex, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal j) { return offerDelete(view, sleDel, j); @@ -94,7 +93,7 @@ removeSignersFromLedger( ApplyView& view, AccountID const& account, uint256 const& delIndex, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal j) { return SignerListSet::removeFromLedger(registry, view, account, j); @@ -106,7 +105,7 @@ removeTicketFromLedger( ApplyView& view, AccountID const& account, uint256 const& delIndex, - std::shared_ptr const&, + SLE::ref, beast::Journal j) { return Transactor::ticketDelete(view, account, delIndex, j); @@ -118,7 +117,7 @@ removeDepositPreauthFromLedger( ApplyView& view, AccountID const&, uint256 const& delIndex, - std::shared_ptr const&, + SLE::ref, beast::Journal j) { return DepositPreauth::removeFromLedger(view, delIndex, j); @@ -130,7 +129,7 @@ removeNFTokenOfferFromLedger( ApplyView& view, AccountID const& account, uint256 const& delIndex, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal) { if (!nft::deleteTokenOffer(view, sleDel)) @@ -145,7 +144,7 @@ removeDIDFromLedger( ApplyView& view, AccountID const& account, uint256 const& delIndex, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal j) { return DIDDelete::deleteSLE(view, sleDel, account, j); @@ -157,7 +156,7 @@ removeOracleFromLedger( ApplyView& view, AccountID const& account, uint256 const&, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal j) { return OracleDelete::deleteOracle(view, sleDel, account, j); @@ -169,7 +168,7 @@ removeCredentialFromLedger( ApplyView& view, AccountID const&, uint256 const&, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal j) { return credentials::deleteSLE(view, sleDel, j); @@ -181,7 +180,7 @@ removeDelegateFromLedger( ApplyView& view, AccountID const&, uint256 const&, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal j) { return DelegateSet::deleteDelegate(view, sleDel, j); @@ -301,7 +300,7 @@ AccountDelete::preclaim(PreclaimContext const& ctx) if (dirIsEmpty(ctx.view, ownerDirKeylet)) return tesSUCCESS; - std::shared_ptr sleDirNode{}; + SLE::const_pointer sleDirNode{}; unsigned int uDirEntry{0}; uint256 dirEntry{beast::kZero}; @@ -368,7 +367,7 @@ AccountDelete::doApply() ownerDirKeylet, [&](LedgerEntryType nodeType, uint256 const& dirEntry, - std::shared_ptr& sleItem) -> std::pair { + SLE::pointer& sleItem) -> std::pair { if (auto deleter = nonObligationDeleter(nodeType)) { TER const result{deleter(ctx_.registry, view(), accountID_, dirEntry, sleItem, j_)}; @@ -417,10 +416,7 @@ AccountDelete::doApply() } void -AccountDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AccountDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/account/AccountSet.cpp b/src/libxrpl/tx/transactors/account/AccountSet.cpp index b52db14720..bc207b39dc 100644 --- a/src/libxrpl/tx/transactors/account/AccountSet.cpp +++ b/src/libxrpl/tx/transactors/account/AccountSet.cpp @@ -26,7 +26,6 @@ #include #include -#include #include namespace xrpl { @@ -643,10 +642,7 @@ AccountSet::doApply() } void -AccountSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AccountSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/account/SetRegularKey.cpp b/src/libxrpl/tx/transactors/account/SetRegularKey.cpp index 66a0bef336..08ccf5e7ab 100644 --- a/src/libxrpl/tx/transactors/account/SetRegularKey.cpp +++ b/src/libxrpl/tx/transactors/account/SetRegularKey.cpp @@ -13,8 +13,6 @@ #include #include -#include - namespace xrpl { XRPAmount @@ -81,10 +79,7 @@ SetRegularKey::doApply() } void -SetRegularKey::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +SetRegularKey::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/account/SignerListSet.cpp b/src/libxrpl/tx/transactors/account/SignerListSet.cpp index 7a5d2c60b0..cedb9ace78 100644 --- a/src/libxrpl/tx/transactors/account/SignerListSet.cpp +++ b/src/libxrpl/tx/transactors/account/SignerListSet.cpp @@ -406,10 +406,7 @@ SignerListSet::writeSignersToSLE(SLE::pointer const& ledgerEntry, std::uint32_t } void -SignerListSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +SignerListSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index 76b274a609..7c9c9d95dc 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -798,21 +798,21 @@ readOrpeekBridge(F&& getter, STXChainBridge const& bridgeSpec) return tryGet(STXChainBridge::ChainType::Issuing); } -std::shared_ptr +SLE::pointer peekBridge(ApplyView& v, STXChainBridge const& bridgeSpec) { return readOrpeekBridge( - [&v](STXChainBridge const& b, STXChainBridge::ChainType ct) -> std::shared_ptr { + [&v](STXChainBridge const& b, STXChainBridge::ChainType ct) -> SLE::pointer { return v.peek(keylet::bridge(b, ct)); }, bridgeSpec); } -std::shared_ptr +SLE::const_pointer readBridge(ReadView const& v, STXChainBridge const& bridgeSpec) { return readOrpeekBridge( - [&v](STXChainBridge const& b, STXChainBridge::ChainType ct) -> std::shared_ptr { + [&v](STXChainBridge const& b, STXChainBridge::ChainType ct) -> SLE::const_pointer { return v.read(keylet::bridge(b, ct)); }, bridgeSpec); @@ -2225,10 +2225,7 @@ XChainCreateAccountCommit::doApply() } void -XChainCreateBridge::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +XChainCreateBridge::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } @@ -2246,10 +2243,7 @@ XChainCreateBridge::finalizeInvariants( } void -BridgeModify::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +BridgeModify::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } @@ -2267,10 +2261,7 @@ BridgeModify::finalizeInvariants( } void -XChainClaim::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +XChainClaim::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } @@ -2283,10 +2274,7 @@ XChainClaim::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, be } void -XChainCommit::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +XChainCommit::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } @@ -2304,10 +2292,7 @@ XChainCommit::finalizeInvariants( } void -XChainCreateClaimID::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +XChainCreateClaimID::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } @@ -2325,10 +2310,7 @@ XChainCreateClaimID::finalizeInvariants( } void -XChainAddClaimAttestation::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +XChainAddClaimAttestation::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } @@ -2346,10 +2328,7 @@ XChainAddClaimAttestation::finalizeInvariants( } void -XChainAddAccountCreateAttestation::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +XChainAddAccountCreateAttestation::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } @@ -2367,10 +2346,7 @@ XChainAddAccountCreateAttestation::finalizeInvariants( } void -XChainCreateAccountCommit::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +XChainCreateAccountCommit::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/check/CheckCancel.cpp b/src/libxrpl/tx/transactors/check/CheckCancel.cpp index 7f1708fd81..d966772191 100644 --- a/src/libxrpl/tx/transactors/check/CheckCancel.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCancel.cpp @@ -14,8 +14,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -102,10 +100,7 @@ CheckCancel::doApply() } void -CheckCancel::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +CheckCancel::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/check/CheckCash.cpp b/src/libxrpl/tx/transactors/check/CheckCash.cpp index af903ff177..c5813ae42d 100644 --- a/src/libxrpl/tx/transactors/check/CheckCash.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCash.cpp @@ -31,7 +31,6 @@ #include #include -#include #include namespace xrpl { @@ -388,7 +387,7 @@ CheckCash::doApply() // Check reserve. Return destination account SLE if enough reserve, // otherwise return nullptr. - auto checkReserve = [&]() -> std::shared_ptr { + auto checkReserve = [&]() -> SLE::pointer { auto sleDst = psb.peek(keylet::account(accountID_)); // Can the account cover the trust line's or MPT reserve? @@ -592,10 +591,7 @@ CheckCash::doApply() } void -CheckCash::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +CheckCash::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/check/CheckCreate.cpp b/src/libxrpl/tx/transactors/check/CheckCreate.cpp index d2d84536de..ff82912a1f 100644 --- a/src/libxrpl/tx/transactors/check/CheckCreate.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCreate.cpp @@ -258,10 +258,7 @@ CheckCreate::doApply() } void -CheckCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +CheckCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp index 4e9857d782..e0ebdd893a 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp @@ -20,8 +20,6 @@ #include #include -#include - namespace xrpl { using namespace credentials; @@ -126,10 +124,7 @@ CredentialAccept::doApply() } void -CredentialAccept::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +CredentialAccept::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp index bcb0a6fefa..acca408a95 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp @@ -179,10 +179,7 @@ CredentialCreate::doApply() } void -CredentialCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +CredentialCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp b/src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp index fcd1848cbe..6bd4ad54c5 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp @@ -16,8 +16,6 @@ #include #include -#include - namespace xrpl { using namespace credentials; @@ -97,10 +95,7 @@ CredentialDelete::doApply() } void -CredentialDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +CredentialDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp index 30d703686c..82fe88aa9f 100644 --- a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp @@ -132,7 +132,7 @@ DelegateSet::doApply() } TER -DelegateSet::deleteDelegate(ApplyView& view, std::shared_ptr const& sle, beast::Journal j) +DelegateSet::deleteDelegate(ApplyView& view, SLE::ref sle, beast::Journal j) { if (!sle) return tecINTERNAL; // LCOV_EXCL_LINE @@ -174,10 +174,7 @@ DelegateSet::deleteDelegate(ApplyView& view, std::shared_ptr const& sle, be } void -DelegateSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +DelegateSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp b/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp index 4d3d97e443..dc6c98f95e 100644 --- a/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp @@ -7,12 +7,11 @@ #include #include -#include #include namespace xrpl { NotTEC -checkTxPermission(std::shared_ptr const& delegate, STTx const& tx) +checkTxPermission(SLE::const_ref delegate, STTx const& tx) { if (!delegate) return terNO_DELEGATE_PERMISSION; @@ -32,7 +31,7 @@ checkTxPermission(std::shared_ptr const& delegate, STTx const& tx) void loadGranularPermission( - std::shared_ptr const& delegate, + SLE::const_ref delegate, TxType const& txType, std::unordered_set& granularPermissions) { diff --git a/src/libxrpl/tx/transactors/dex/AMMBid.cpp b/src/libxrpl/tx/transactors/dex/AMMBid.cpp index b3b41fbfa2..a98f439d0a 100644 --- a/src/libxrpl/tx/transactors/dex/AMMBid.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMBid.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -379,10 +378,7 @@ AMMBid::doApply() } void -AMMBid::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AMMBid::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/AMMClawback.cpp b/src/libxrpl/tx/transactors/dex/AMMClawback.cpp index 43abc6ae29..b94e97e931 100644 --- a/src/libxrpl/tx/transactors/dex/AMMClawback.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMClawback.cpp @@ -27,7 +27,6 @@ #include #include -#include #include #include @@ -383,10 +382,7 @@ AMMClawback::equalWithdrawMatchingOneAmount( } void -AMMClawback::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AMMClawback::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp index 60508eab85..9c1a5cfacb 100644 --- a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp @@ -389,10 +389,7 @@ AMMCreate::doApply() } void -AMMCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AMMCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/AMMDelete.cpp b/src/libxrpl/tx/transactors/dex/AMMDelete.cpp index 8a8cab1c03..e2ecec8242 100644 --- a/src/libxrpl/tx/transactors/dex/AMMDelete.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDelete.cpp @@ -15,8 +15,6 @@ #include #include -#include - namespace xrpl { bool @@ -67,10 +65,7 @@ AMMDelete::doApply() } void -AMMDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AMMDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp index f45529f617..91858e3cd7 100644 --- a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -1014,10 +1013,7 @@ AMMDeposit::equalDepositInEmptyState( } void -AMMDeposit::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AMMDeposit::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/AMMVote.cpp b/src/libxrpl/tx/transactors/dex/AMMVote.cpp index 391f7e1ecc..23604d46cc 100644 --- a/src/libxrpl/tx/transactors/dex/AMMVote.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMVote.cpp @@ -22,7 +22,6 @@ #include #include -#include #include #include @@ -249,10 +248,7 @@ AMMVote::doApply() } void -AMMVote::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AMMVote::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index 352f637f2f..e57f8558ff 100644 --- a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -758,7 +757,7 @@ AMMWithdraw::equalWithdrawTokens( std::pair AMMWithdraw::deleteAMMAccountIfEmpty( Sandbox& sb, - std::shared_ptr const ammSle, + SLE::pointer const ammSle, STAmount const& lpTokenBalance, Asset const& asset1, Asset const& asset2, @@ -1137,10 +1136,7 @@ AMMWithdraw::isWithdrawAll(STTx const& tx) return WithdrawAll::No; } void -AMMWithdraw::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AMMWithdraw::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/OfferCancel.cpp b/src/libxrpl/tx/transactors/dex/OfferCancel.cpp index 42692b59cc..0dea5fa967 100644 --- a/src/libxrpl/tx/transactors/dex/OfferCancel.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCancel.cpp @@ -10,8 +10,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -70,10 +68,7 @@ OfferCancel::doApply() } void -OfferCancel::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +OfferCancel::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp index f982837c5e..547d40e7b8 100644 --- a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -559,7 +559,7 @@ OfferCreate::formatAmount(STAmount const& amount) TER OfferCreate::applyHybrid( Sandbox& sb, - std::shared_ptr sleOffer, + STLedgerEntry::pointer sleOffer, Keylet const& offerKey, STAmount const& saTakerPays, STAmount const& saTakerGets, @@ -990,10 +990,7 @@ OfferCreate::doApply() } void -OfferCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +OfferCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/did/DIDDelete.cpp b/src/libxrpl/tx/transactors/did/DIDDelete.cpp index 4b4fb47169..90aa21d8a1 100644 --- a/src/libxrpl/tx/transactors/did/DIDDelete.cpp +++ b/src/libxrpl/tx/transactors/did/DIDDelete.cpp @@ -16,8 +16,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -37,11 +35,7 @@ DIDDelete::deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner) } TER -DIDDelete::deleteSLE( - ApplyView& view, - std::shared_ptr sle, - AccountID const owner, - beast::Journal j) +DIDDelete::deleteSLE(ApplyView& view, SLE::pointer sle, AccountID const owner, beast::Journal j) { // Remove object from owner directory if (!view.dirRemove(keylet::ownerDir(owner), (*sle)[sfOwnerNode], sle->key(), true)) @@ -71,10 +65,7 @@ DIDDelete::doApply() } void -DIDDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +DIDDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/did/DIDSet.cpp b/src/libxrpl/tx/transactors/did/DIDSet.cpp index a930c3c754..1392581bb0 100644 --- a/src/libxrpl/tx/transactors/did/DIDSet.cpp +++ b/src/libxrpl/tx/transactors/did/DIDSet.cpp @@ -63,7 +63,7 @@ DIDSet::preflight(PreflightContext const& ctx) } static TER -addSLE(ApplyContext& ctx, std::shared_ptr const& sle, AccountID const& owner) +addSLE(ApplyContext& ctx, SLE::ref sle, AccountID const& owner) { auto const sleAccount = ctx.view().peek(keylet::account(owner)); if (!sleAccount) @@ -150,10 +150,7 @@ DIDSet::doApply() } void -DIDSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +DIDSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp index 123f83a1a6..b8f4604d73 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp @@ -23,7 +23,6 @@ #include #include -#include #include namespace xrpl { @@ -220,10 +219,7 @@ EscrowCancel::doApply() } void -EscrowCancel::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +EscrowCancel::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp index 4de302db3e..0a12e2d1bc 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp @@ -541,10 +541,7 @@ EscrowCreate::doApply() } void -EscrowCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +EscrowCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp index 13bd4b1682..4cda867b48 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp @@ -30,7 +30,6 @@ #include #include -#include #include #include @@ -401,10 +400,7 @@ EscrowFinish::doApply() } void -EscrowFinish::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +EscrowFinish::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp index 11095fdebe..0e1a4b3a3d 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp @@ -28,7 +28,6 @@ #include #include -#include #include #include @@ -377,10 +376,7 @@ LoanBrokerCoverClawback::doApply() } void -LoanBrokerCoverClawback::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanBrokerCoverClawback::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp index e84f277f5b..537996ba57 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp @@ -16,8 +16,6 @@ #include #include -#include - namespace xrpl { bool @@ -181,10 +179,7 @@ LoanBrokerCoverDeposit::doApply() } void -LoanBrokerCoverDeposit::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanBrokerCoverDeposit::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp index f4c95541f6..fea6f3b9cb 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp @@ -20,8 +20,6 @@ #include #include -#include - namespace xrpl { bool @@ -204,10 +202,7 @@ LoanBrokerCoverWithdraw::doApply() } void -LoanBrokerCoverWithdraw::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanBrokerCoverWithdraw::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp index 6b77914370..f3c000bf0b 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp @@ -18,8 +18,6 @@ #include #include -#include - namespace xrpl { bool @@ -206,10 +204,7 @@ LoanBrokerDelete::doApply() } void -LoanBrokerDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanBrokerDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp index 6e3d2ad0a8..2dc003eb7f 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp @@ -279,10 +279,7 @@ LoanBrokerSet::doApply() } void -LoanBrokerSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanBrokerSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp index 813700bd7a..d4ec92a9fb 100644 --- a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp @@ -16,8 +16,6 @@ #include #include -#include - namespace xrpl { bool @@ -142,10 +140,7 @@ LoanDelete::doApply() } void -LoanDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanManage.cpp b/src/libxrpl/tx/transactors/lending/LoanManage.cpp index d3165006b5..2b5c9d25f6 100644 --- a/src/libxrpl/tx/transactors/lending/LoanManage.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanManage.cpp @@ -26,8 +26,6 @@ #include #include -#include - namespace xrpl { bool @@ -435,10 +433,7 @@ LoanManage::doApply() } void -LoanManage::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanManage::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanPay.cpp b/src/libxrpl/tx/transactors/lending/LoanPay.cpp index 65220573de..a0a1479bdb 100644 --- a/src/libxrpl/tx/transactors/lending/LoanPay.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanPay.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include namespace xrpl { @@ -831,10 +830,7 @@ LoanPay::doApply() } void -LoanPay::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanPay::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanSet.cpp b/src/libxrpl/tx/transactors/lending/LoanSet.cpp index 561ab6b2aa..573f700f51 100644 --- a/src/libxrpl/tx/transactors/lending/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanSet.cpp @@ -656,10 +656,7 @@ LoanSet::doApply() } void -LoanSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp index b80d282d70..6a82a15044 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp @@ -21,7 +21,6 @@ #include #include -#include #include #include @@ -55,7 +54,7 @@ TER NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { auto const checkOffer = - [&ctx](std::optional id) -> std::pair, TER> { + [&ctx](std::optional id) -> std::pair { if (id) { if (id->isZero()) @@ -403,7 +402,7 @@ NFTokenAcceptOffer::transferNFToken( } TER -NFTokenAcceptOffer::acceptOffer(std::shared_ptr const& offer) +NFTokenAcceptOffer::acceptOffer(SLE::ref offer) { bool const isSell = offer->isFlag(lsfSellNFToken); AccountID const owner = (*offer)[sfOwner]; @@ -441,7 +440,7 @@ TER NFTokenAcceptOffer::doApply() { auto const loadToken = [this](std::optional const& id) { - std::shared_ptr sle; + SLE::pointer sle; if (id) sle = view().peek(keylet::nftoffer(*id)); return sle; @@ -456,8 +455,7 @@ NFTokenAcceptOffer::doApply() { bool foundExpired = false; - auto const deleteOfferIfExpired = - [this, &foundExpired](std::shared_ptr const& offer) -> TER { + auto const deleteOfferIfExpired = [this, &foundExpired](SLE::ref offer) -> TER { if (offer && hasExpired(view(), (*offer)[~sfExpiration])) { JLOG(j_.trace()) << "Offer is expired, deleting: " << offer->key(); @@ -569,10 +567,7 @@ NFTokenAcceptOffer::doApply() } void -NFTokenAcceptOffer::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +NFTokenAcceptOffer::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp b/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp index 871a8e706a..d33b1af8c2 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp @@ -13,8 +13,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -95,10 +93,7 @@ NFTokenBurn::doApply() } void -NFTokenBurn::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +NFTokenBurn::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp index 924dc49269..1614f90202 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp @@ -16,8 +16,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -98,10 +96,7 @@ NFTokenCancelOffer::doApply() } void -NFTokenCancelOffer::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +NFTokenCancelOffer::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp index 9c7fe7d5ef..1948f3803d 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp @@ -14,8 +14,6 @@ #include #include -#include - namespace xrpl { std::uint32_t @@ -91,10 +89,7 @@ NFTokenCreateOffer::doApply() } void -NFTokenCreateOffer::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +NFTokenCreateOffer::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp index e7faa30df8..bd7413d50b 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp @@ -27,7 +27,6 @@ #include #include #include // IWYU pragma: keep -#include #include namespace xrpl { @@ -344,10 +343,7 @@ NFTokenMint::doApply() } void -NFTokenMint::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +NFTokenMint::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp b/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp index 7e3eeeefee..32ba362ded 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp @@ -14,8 +14,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -69,10 +67,7 @@ NFTokenModify::doApply() } void -NFTokenModify::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +NFTokenModify::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp index 839b2b4e24..9a26816155 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp @@ -13,8 +13,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -51,7 +49,7 @@ OracleDelete::preclaim(PreclaimContext const& ctx) TER OracleDelete::deleteOracle( ApplyView& view, - std::shared_ptr const& sle, + SLE::ref sle, AccountID const& account, beast::Journal j) { @@ -89,10 +87,7 @@ OracleDelete::doApply() } void -OracleDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +OracleDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp index 66ce95f46c..12d826e54d 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp @@ -328,10 +328,7 @@ OracleSet::doApply() } void -OracleSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +OracleSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp index 8b4ad2f025..7e950dc743 100644 --- a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp +++ b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp @@ -298,10 +298,7 @@ DepositPreauth::removeFromLedger(ApplyView& view, uint256 const& preauthIndex, b } void -DepositPreauth::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +DepositPreauth::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/payment/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp index d7ad5d3b3c..805ebe3684 100644 --- a/src/libxrpl/tx/transactors/payment/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -679,10 +679,7 @@ Payment::doApply() } void -Payment::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +Payment::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp index cc99b8f62d..b1fe5e24bc 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp @@ -23,7 +23,6 @@ #include #include -#include #include namespace xrpl { @@ -200,10 +199,7 @@ PaymentChannelClaim::doApply() } void -PaymentChannelClaim::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +PaymentChannelClaim::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp index 7ce25b6d15..63dbe01944 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp @@ -185,10 +185,7 @@ PaymentChannelCreate::doApply() } void -PaymentChannelCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +PaymentChannelCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp index 41906aa3da..bcb8a91c96 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp @@ -18,8 +18,6 @@ #include #include -#include - namespace xrpl { TxConsequences @@ -107,10 +105,7 @@ PaymentChannelFund::doApply() } void -PaymentChannelFund::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +PaymentChannelFund::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp index e36a0eb584..7eb3f282b9 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp @@ -12,8 +12,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -74,10 +72,7 @@ PermissionedDomainDelete::doApply() } void -PermissionedDomainDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +PermissionedDomainDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp index ff4019ec59..0e71ceada1 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp @@ -133,10 +133,7 @@ PermissionedDomainSet::doApply() } void -PermissionedDomainSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +PermissionedDomainSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/system/Batch.cpp b/src/libxrpl/tx/transactors/system/Batch.cpp index b9440e3273..64a62ac273 100644 --- a/src/libxrpl/tx/transactors/system/Batch.cpp +++ b/src/libxrpl/tx/transactors/system/Batch.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -518,10 +517,7 @@ Batch::doApply() } void -Batch::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +Batch::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/system/Change.cpp b/src/libxrpl/tx/transactors/system/Change.cpp index 92a06fd807..38fee5bf92 100644 --- a/src/libxrpl/tx/transactors/system/Change.cpp +++ b/src/libxrpl/tx/transactors/system/Change.cpp @@ -409,10 +409,7 @@ Change::applyUNLModify() } void -Change::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +Change::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp b/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp index 222e5dce7a..65c32b788c 100644 --- a/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp +++ b/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp @@ -16,7 +16,6 @@ #include #include -#include #include namespace xrpl { @@ -153,10 +152,7 @@ LedgerStateFix::doApply() } void -LedgerStateFix::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LedgerStateFix::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/system/TicketCreate.cpp b/src/libxrpl/tx/transactors/system/TicketCreate.cpp index f442ac223b..5be00fe76c 100644 --- a/src/libxrpl/tx/transactors/system/TicketCreate.cpp +++ b/src/libxrpl/tx/transactors/system/TicketCreate.cpp @@ -135,10 +135,7 @@ TicketCreate::doApply() } void -TicketCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +TicketCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/token/Clawback.cpp b/src/libxrpl/tx/transactors/token/Clawback.cpp index 8532f97995..06132c1c97 100644 --- a/src/libxrpl/tx/transactors/token/Clawback.cpp +++ b/src/libxrpl/tx/transactors/token/Clawback.cpp @@ -23,7 +23,6 @@ #include #include -#include #include namespace xrpl { @@ -275,10 +274,7 @@ Clawback::doApply() } void -Clawback::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +Clawback::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp b/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp index 9b0c8d2b56..332d160cac 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp @@ -15,8 +15,6 @@ #include #include -#include - namespace xrpl { std::uint32_t @@ -48,7 +46,7 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx) // `holderID` is NOT used if (!holderID) { - std::shared_ptr const sleMpt = + SLE::const_pointer const sleMpt = ctx.view.read(keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], accountID)); // There is an edge case where all holders have zero balance, issuance @@ -156,10 +154,7 @@ MPTokenAuthorize::doApply() } void -MPTokenAuthorize::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +MPTokenAuthorize::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index 64d0b01f5e..94a5ca848d 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -195,10 +195,7 @@ MPTokenIssuanceCreate::doApply() } void -MPTokenIssuanceCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +MPTokenIssuanceCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp index 46811508b7..1029c25813 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp @@ -9,8 +9,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -59,10 +57,7 @@ MPTokenIssuanceDestroy::doApply() } void -MPTokenIssuanceDestroy::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +MPTokenIssuanceDestroy::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp index d8b62acf3f..9b25531161 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include namespace xrpl { @@ -270,7 +269,7 @@ MPTokenIssuanceSet::doApply() auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID]; auto const holderID = ctx_.tx[~sfHolder]; auto const domainID = ctx_.tx[~sfDomainID]; - std::shared_ptr sle; + SLE::pointer sle; if (holderID) { @@ -373,10 +372,7 @@ MPTokenIssuanceSet::doApply() } void -MPTokenIssuanceSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +MPTokenIssuanceSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/token/TrustSet.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp index a8ea786347..1d2bc96693 100644 --- a/src/libxrpl/tx/transactors/token/TrustSet.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -27,7 +27,6 @@ #include #include -#include #include namespace { @@ -669,10 +668,7 @@ TrustSet::doApply() } void -TrustSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +TrustSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/vault/VaultClawback.cpp b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp index b5b2c1f384..eb12905467 100644 --- a/src/libxrpl/tx/transactors/vault/VaultClawback.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -61,7 +60,7 @@ VaultClawback::preflight(PreflightContext const& ctx) [[nodiscard]] STAmount clawbackAmount( - std::shared_ptr const& vault, + SLE::const_ref vault, std::optional const& maybeAmount, AccountID const& account) { @@ -221,8 +220,8 @@ VaultClawback::preclaim(PreclaimContext const& ctx) Expected, TER> VaultClawback::assetsToClawback( - std::shared_ptr const& vault, - std::shared_ptr const& sleShareIssuance, + SLE::ref vault, + SLE::const_ref sleShareIssuance, AccountID const& holder, STAmount const& clawbackAmount) { @@ -452,10 +451,7 @@ VaultClawback::doApply() } void -VaultClawback::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +VaultClawback::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp index 7490f44619..4163753014 100644 --- a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp @@ -263,10 +263,7 @@ VaultCreate::doApply() } void -VaultCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +VaultCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp index 438896e7ed..030a7e971c 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp @@ -17,8 +17,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -213,10 +211,7 @@ VaultDelete::doApply() } void -VaultDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +VaultDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp index 50d165a2ba..c08d1e957c 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp @@ -23,7 +23,6 @@ #include #include -#include #include namespace xrpl { @@ -348,10 +347,7 @@ VaultDeposit::doApply() } void -VaultDeposit::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +VaultDeposit::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/vault/VaultSet.cpp b/src/libxrpl/tx/transactors/vault/VaultSet.cpp index f384fd3fb1..6d0ade6e52 100644 --- a/src/libxrpl/tx/transactors/vault/VaultSet.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultSet.cpp @@ -15,8 +15,6 @@ #include #include -#include - namespace xrpl { bool @@ -180,10 +178,7 @@ VaultSet::doApply() } void -VaultSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +VaultSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp index c52d94ac0b..cfcf79fdba 100644 --- a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp @@ -23,7 +23,6 @@ #include #include -#include #include namespace xrpl { @@ -368,10 +367,7 @@ VaultWithdraw::doApply() } void -VaultWithdraw::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +VaultWithdraw::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/test/app/CheckMPT_test.cpp b/src/test/app/CheckMPT_test.cpp index 1ca24051dd..861a115fc9 100644 --- a/src/test/app/CheckMPT_test.cpp +++ b/src/test/app/CheckMPT_test.cpp @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include @@ -54,11 +53,11 @@ namespace xrpl { class CheckMPT_test : public beast::unit_test::Suite { // Helper function that returns the Checks on an account. - static std::vector> + static std::vector checksOnAccount(test::jtx::Env& env, test::jtx::Account account) { - std::vector> result; - forEachItem(*env.current(), account, [&result](std::shared_ptr const& sle) { + std::vector result; + forEachItem(*env.current(), account, [&result](SLE::const_ref sle) { if (sle && sle->getType() == ltCHECK) result.push_back(sle); }); diff --git a/src/test/app/Check_test.cpp b/src/test/app/Check_test.cpp index 1d5861136c..9b814a24b1 100644 --- a/src/test/app/Check_test.cpp +++ b/src/test/app/Check_test.cpp @@ -43,7 +43,6 @@ #include #include -#include #include #include @@ -58,11 +57,11 @@ class Check_test : public beast::unit_test::Suite } // Helper function that returns the Checks on an account. - static std::vector> + static std::vector checksOnAccount(test::jtx::Env& env, test::jtx::Account account) { - std::vector> result; - forEachItem(*env.current(), account, [&result](std::shared_ptr const& sle) { + std::vector result; + forEachItem(*env.current(), account, [&result](SLE::const_ref sle) { if (sle && sle->getType() == ltCHECK) result.push_back(sle); }); diff --git a/src/test/app/FlowMPT_test.cpp b/src/test/app/FlowMPT_test.cpp index e1d6f512d2..2e224a7eb1 100644 --- a/src/test/app/FlowMPT_test.cpp +++ b/src/test/app/FlowMPT_test.cpp @@ -37,7 +37,6 @@ #include #include -#include #include #include #include @@ -433,7 +432,7 @@ struct FlowMPT_test : public beast::unit_test::Suite env.require(Balance(bob, eur(999))); // Show that bob's USD offer is now a blocker. - std::shared_ptr const usdOffer = env.le(bobUsdOffer); + SLE::const_pointer const usdOffer = env.le(bobUsdOffer); if (BEAST_EXPECT(usdOffer)) { std::uint64_t const bookRate = [&usdOffer]() { @@ -730,11 +729,11 @@ struct FlowMPT_test : public beast::unit_test::Suite } // Helper function that returns the Offers on an account. - static std::vector> + static std::vector offersOnAccount(jtx::Env& env, jtx::Account account) { - std::vector> result; - forEachItem(*env.current(), account, [&result](std::shared_ptr const& sle) { + std::vector result; + forEachItem(*env.current(), account, [&result](SLE::const_ref sle) { if (sle->getType() == ltOFFER) result.push_back(sle); }); @@ -1792,7 +1791,7 @@ struct FlowMPT_test : public beast::unit_test::Suite // but OutstandingAmount is 300USD because gw's sell offer is balanced out by // gw's buy offer. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 400, 400, 400, 400, 300, 100, 100, 100, 1100, 0, false}, + { .maxAmt=400, .sendMax=400, .dstTrustLimit=400, .dstExpectEUR=400, .outstandingUSD=300, .expEdBuyUSD=100, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1100, .expOffersGw=0, .lastGwBuyUSD=false}, // Sell USD: alice, carol, bob, gw are consumed. // Buy USD: john, gw, dan, ed (partially) are consumed. // gw's sell USD is partially consumed because there is available balance (50USD). @@ -1801,32 +1800,32 @@ struct FlowMPT_test : public beast::unit_test::Suite // gw's offer is removed from the order book because it's partially consumed and // the remaining offer is unfunded. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 350, 400, 400, 350, 250, 50, 100, 100, 1050, 0, false}, + { .maxAmt=350, .sendMax=400, .dstTrustLimit=400, .dstExpectEUR=350, .outstandingUSD=250, .expEdBuyUSD=50, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=0, .lastGwBuyUSD=false}, // Sell USD: alice, carol, bob are consumed; gw's is unfunded // since OutstandingAmount is initially at MaximumAmount. // Buy USD: john, gw, dan are consumed; ed's remains on the order // book since 300USD is the sell limit. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 300, 400, 400, 300, 200, 0, 100, 100, 1000, 0, false}, + { .maxAmt=300, .sendMax=400, .dstTrustLimit=400, .dstExpectEUR=300, .outstandingUSD=200, .expEdBuyUSD=0, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=false}, // Same as above. bill's trustline limit sets the output to 300USD. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 300, 400, 300, 300, 200, 0, 100, 100, 1000, 0, false}, + { .maxAmt=300, .sendMax=400, .dstTrustLimit=300, .dstExpectEUR=300, .outstandingUSD=200, .expEdBuyUSD=0, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=false}, // Sell USD: alice, carol, bob are consumed; gw's removed from // the order book since it's unfunded. // Buy USD: john, gw, dan are consumed; ed's remains on the order // book since 300USD is the limit. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 300, 400, 300, 300, 200, 0, 100, 100, 1000, 0, true}, + { .maxAmt=300, .sendMax=400, .dstTrustLimit=300, .dstExpectEUR=300, .outstandingUSD=200, .expEdBuyUSD=0, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=true}, // Sell USD: alice, carol are consumed; gw's removed from // the order book in rev pass since it's unfunded; bob's // remains on the order book. // Buy USD: john, gw; ed's, dan's remains on the order // book since 300USD is the limit. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 300, 200, 300, 200, 200, 0, 0, 0, 1000, 0, false}, + { .maxAmt=300, .sendMax=200, .dstTrustLimit=300, .dstExpectEUR=200, .outstandingUSD=200, .expEdBuyUSD=0, .expDanBuyUSD=0, .expBobSellUSD=0, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=false}, // Same as three tests above since limited by buy 300USD (gw offer is unfunded) //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 300, 380, 400, 300, 200, 0, 100, 100, 1000, 0, false}, + { .maxAmt=300, .sendMax=380, .dstTrustLimit=400, .dstExpectEUR=300, .outstandingUSD=200, .expEdBuyUSD=0, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=false}, }; // clang-format on for (auto const& t : tests) @@ -1912,26 +1911,26 @@ struct FlowMPT_test : public beast::unit_test::Suite // Gw gets 300USD from alice; carol and bob buy 200USD, // therefore OutstandingAmount is 200. //*maxAmt sendMax gwOffer dstXRP outstandingUSD bobBuy gwXRP offersGw lastGw - { 300, 300, 100, 1300, 200, 100, 900, 0, false}, + { .maxAmt=300, .sendMax=300, .gwOffer=100, .dstExpectXRP=1300, .outstandingUSD=200, .expBobBuyUSD=100, .expGwXRP=900, .expOffersGw=0, .lastGwBuyUSD=false}, // Same as above. Gw offer location in the order book doesn't matter //*maxAmt sendMax gwOffer dstXRP outstandingUSD bobBuy gwXRP offersGw lastGw - { 300, 300, 100, 1300, 200, 100, 900, 0, true}, + { .maxAmt=300, .sendMax=300, .gwOffer=100, .dstExpectXRP=1300, .outstandingUSD=200, .expBobBuyUSD=100, .expGwXRP=900, .expOffersGw=0, .lastGwBuyUSD=true}, // Buy USD: carol, gw are consumed. bob's offer remains on the order book. // Gw gets 300USD from alice; carol buys 100USD, // therefore OutstandingAmount is 100. //*maxAmt sendMax gwOffer dstXRP outstandingUSD bobBuy gwXRP offersGw lastGw - { 300, 300, 200, 1300, 100, 0, 800, 0, false}, + { .maxAmt=300, .sendMax=300, .gwOffer=200, .dstExpectXRP=1300, .outstandingUSD=100, .expBobBuyUSD=0, .expGwXRP=800, .expOffersGw=0, .lastGwBuyUSD=false}, // Buy USD: carol, bob are consumed; gw's is partially consumed (100/100) since it's last. // Gw gets 300USD from alice; carol and bob buy 200USD, // therefore OutstandingAmount is 200. //*maxAmt sendMax gwOffer dstXRP outstandingUSD bobBuy gwXRP offersGw lastGw - { 300, 300, 200, 1300, 200, 100, 900, 1, true}, + { .maxAmt=300, .sendMax=300, .gwOffer=200, .dstExpectXRP=1300, .outstandingUSD=200, .expBobBuyUSD=100, .expGwXRP=900, .expOffersGw=1, .lastGwBuyUSD=true}, // Buy USD: carol, bob are consumed; gw's is partially consumed (50/50) since it's last // and sendMax limits the output. // Gw gets 250USD from alice; carol and bob buy 200USD, alice has 50USD left, // therefore OutstandingAmount is 200. //*maxAmt sendMax gwOffer dstXRP outstandingUSD bobBuy gwXRP offersGw lastGw - { 300, 250, 200, 1250, 250, 100, 950, 1, true}, + { .maxAmt=300, .sendMax=250, .gwOffer=200, .dstExpectXRP=1250, .outstandingUSD=250, .expBobBuyUSD=100, .expGwXRP=950, .expOffersGw=1, .lastGwBuyUSD=true}, }; // clang-format on for (auto const& t : tests) @@ -2024,10 +2023,10 @@ struct FlowMPT_test : public beast::unit_test::Suite // Sell USD: carol, gw, bob are consumed. // ed buys 300USD from carol, gw, bob therefore OutstandingAmount is 300. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 300, 0, 100, 300, 300, 700, 100, 1100, 0, false}, + { .maxAmt=300, .sendMax=300, .initDst=0, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=700, .expBobSellUSD=100, .expGwXRP=1100, .expOffersGw=0, .lastGwBuyUSD=false}, // Same as above. Gw offer location in the order book doesn't matter //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 300, 0, 100, 300, 300, 700, 100, 1100, 0, true}, + { .maxAmt=300, .sendMax=300, .initDst=0, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=700, .expBobSellUSD=100, .expGwXRP=1100, .expOffersGw=0, .lastGwBuyUSD=true}, // Sell USD: carol, bob are consumed, gw is partially consumed. // ed buys 200 from carol and bob and 50 from gw because gw can only issue 50 // (300(max) - 200(carol+bob) - 50(ed)). ed buys 250 from carol, gw, bob and has 50 initially, @@ -2035,33 +2034,33 @@ struct FlowMPT_test : public beast::unit_test::Suite // gw's offer is removed from the order book because it's partially consumed and the remaining // offer is unfunded. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 300, 50, 100, 300, 300, 750, 100, 1050, 0, false}, + { .maxAmt=300, .sendMax=300, .initDst=50, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=750, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=0, .lastGwBuyUSD=false}, // Same as above. Gw offer location in the order book doesn't matter. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 300, 50, 100, 300, 300, 750, 100, 1050, 0, true}, + { .maxAmt=300, .sendMax=300, .initDst=50, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=750, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=0, .lastGwBuyUSD=true}, // Same as above. Gw offer size doesn't matter. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 300, 50, 200, 300, 300, 750, 100, 1050, 0, true}, + { .maxAmt=300, .sendMax=300, .initDst=50, .gwOffer=200, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=750, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=0, .lastGwBuyUSD=true}, // Sell USD: carol, gw are consumed, bob is partially consumed. // ed buys 200 from carol and gw and 50 form bob because of sendMax limit. bob keeps 50, // therefore OutstandingAmount is 300. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 250, 0, 100, 250, 300, 750, 50, 1100, 0, false}, + { .maxAmt=300, .sendMax=250, .initDst=0, .gwOffer=100, .dstExpectUSD=250, .outstandingUSD=300, .expAliceXRP=750, .expBobSellUSD=50, .expGwXRP=1100, .expOffersGw=0, .lastGwBuyUSD=false}, // Sell USD: carol, bob are consumed, gw is partially consumed because of sendMax limit. // ed buys 200 from carol and bob and 50 from gw. Therefore, OutstandingAmount is 250. // gw's offer remains on the order book because it's partially consumed and has more funds. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 250, 0, 100, 250, 250, 750, 100, 1050, 1, true}, + { .maxAmt=300, .sendMax=250, .initDst=0, .gwOffer=100, .dstExpectUSD=250, .outstandingUSD=250, .expAliceXRP=750, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=1, .lastGwBuyUSD=true}, // Sell USD: carol, bob are consumed, gw is partially consumed because of sendMax limit, also // there is only 50 available to issue. ed buys 200 from carol and bob and 50 from gw, plus // he has initially 50, therefore OutstandingAmount is 300. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 250, 50, 100, 300, 300, 750, 100, 1050, 0, true}, + { .maxAmt=300, .sendMax=250, .initDst=50, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=750, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=0, .lastGwBuyUSD=true}, // Sell USD: carol, bob are consumed, gw is not consumed because there is not available funds // to issue. ed buys 200 from carol and bob and, plus he has initially 100, // therefore OutstandingAmount is 300. gw offer is removed because it's unfunded. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 250, 100, 100, 300, 300, 800, 100, 1000, 0, true}, + { .maxAmt=300, .sendMax=250, .initDst=100, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=800, .expBobSellUSD=100, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=true}, }; // clang-format on for (auto const& t : tests) diff --git a/src/test/app/Flow_test.cpp b/src/test/app/Flow_test.cpp index c6f4cf59d1..5f56a0ceb1 100644 --- a/src/test/app/Flow_test.cpp +++ b/src/test/app/Flow_test.cpp @@ -43,7 +43,6 @@ #include #include -#include #include #include #include @@ -572,7 +571,7 @@ struct Flow_test : public beast::unit_test::Suite env.require(Balance(bob, eur(999))); // Show that bob's USD offer is now a blocker. - std::shared_ptr const usdOffer = env.le(bobUsdOffer); + SLE::const_pointer const usdOffer = env.le(bobUsdOffer); if (BEAST_EXPECT(usdOffer)) { std::uint64_t const bookRate = [&usdOffer]() { @@ -711,11 +710,11 @@ struct Flow_test : public beast::unit_test::Suite } // Helper function that returns the Offers on an account. - static std::vector> + static std::vector offersOnAccount(jtx::Env& env, jtx::Account account) { - std::vector> result; - forEachItem(*env.current(), account, [&result](std::shared_ptr const& sle) { + std::vector result; + forEachItem(*env.current(), account, [&result](SLE::const_ref sle) { if (sle->getType() == ltOFFER) result.push_back(sle); }); diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 432dccce61..3654036869 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -1277,7 +1277,7 @@ class Invariants_test : public beast::unit_test::Suite }); } - static std::shared_ptr + static SLE::pointer createPermissionedDomain( ApplyContext& ac, test::jtx::Account const& a1, diff --git a/src/test/app/OfferMPT_test.cpp b/src/test/app/OfferMPT_test.cpp index e9366f7c32..e0f2f4eab0 100644 --- a/src/test/app/OfferMPT_test.cpp +++ b/src/test/app/OfferMPT_test.cpp @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include @@ -721,11 +720,11 @@ public: } // Helper function that returns the Offers on an account. - static std::vector> + static std::vector offersOnAccount(jtx::Env& env, jtx::Account account) { - std::vector> result; - forEachItem(*env.current(), account, [&result](std::shared_ptr const& sle) { + std::vector result; + forEachItem(*env.current(), account, [&result](SLE::const_ref sle) { if (sle->getType() == ltOFFER) result.push_back(sle); }); @@ -3731,9 +3730,7 @@ public: auto const offerCount = std::distance( actorOffers.begin(), std::remove_if( - actorOffers.begin(), - actorOffers.end(), - [](std::shared_ptr& offer) { + actorOffers.begin(), actorOffers.end(), [](SLE::const_pointer& offer) { return (*offer)[sfTakerGets].signum() == 0; })); BEAST_EXPECT(offerCount == actor.offers); @@ -3903,9 +3900,7 @@ public: auto const offerCount = std::distance( actorOffers.begin(), std::remove_if( - actorOffers.begin(), - actorOffers.end(), - [](std::shared_ptr& offer) { + actorOffers.begin(), actorOffers.end(), [](SLE::const_pointer& offer) { return (*offer)[sfTakerGets].signum() == 0; })); BEAST_EXPECT(offerCount == actor.offers); @@ -4239,15 +4234,13 @@ public: } // Helper function that returns offers on an account sorted by sequence. - static std::vector> + static std::vector sortedOffersOnAccount(jtx::Env& env, jtx::Account const& acct) { - std::vector> offers{offersOnAccount(env, acct)}; - std::ranges::sort( - offers, - [](std::shared_ptr const& rhs, std::shared_ptr const& lhs) { - return (*rhs)[sfSequence] < (*lhs)[sfSequence]; - }); + std::vector offers{offersOnAccount(env, acct)}; + std::ranges::sort(offers, [](SLE::const_ref rhs, SLE::const_ref lhs) { + return (*rhs)[sfSequence] < (*lhs)[sfSequence]; + }); return offers; } @@ -4692,14 +4685,14 @@ public: // IOU/IOU, XRP/IOU, IOU/XRP offers have TickSize logic unchanged // IOU/MPT, MPT/IOU have TickSize logic applied to adjust IOU only std::vector const tests = { - {getIOU, getIOU, 10, 30}, - {getIOU, getXRP, 10, 30'000'000}, - {getXRP, getIOU, 10'000'000, 30}, - {getMPT, getXRP, 10'000'000, 30'000'000}, - {getXRP, getMPT, 10'000'000, 30'000'000}, - {getIOU, getMPT, 10, 30'000'000}, - {getMPT, getIOU, 10'000'000, 30}, - {getMPT, getMPT, 10'000'000, 30'000'000}}; + {.toAsset1 = getIOU, .toAsset2 = getIOU, .val1 = 10, .val2 = 30}, + {.toAsset1 = getIOU, .toAsset2 = getXRP, .val1 = 10, .val2 = 30'000'000}, + {.toAsset1 = getXRP, .toAsset2 = getIOU, .val1 = 10'000'000, .val2 = 30}, + {.toAsset1 = getMPT, .toAsset2 = getXRP, .val1 = 10'000'000, .val2 = 30'000'000}, + {.toAsset1 = getXRP, .toAsset2 = getMPT, .val1 = 10'000'000, .val2 = 30'000'000}, + {.toAsset1 = getIOU, .toAsset2 = getMPT, .val1 = 10, .val2 = 30'000'000}, + {.toAsset1 = getMPT, .toAsset2 = getIOU, .val1 = 10'000'000, .val2 = 30}, + {.toAsset1 = getMPT, .toAsset2 = getMPT, .val1 = 10'000'000, .val2 = 30'000'000}}; for (TestInfo const& t : tests) { Env env{*this, features}; @@ -4731,7 +4724,7 @@ public: env(offer(alice, xts(t.val2), xxx(t.val1)), Json(jss::Flags, tfSell)); std::map> offers; - forEachItem(*env.current(), alice, [&](std::shared_ptr const& sle) { + forEachItem(*env.current(), alice, [&](SLE::const_ref sle) { if (sle->getType() == ltOFFER) { offers.emplace( diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp index 811a18dda5..83c58884e0 100644 --- a/src/test/app/Offer_test.cpp +++ b/src/test/app/Offer_test.cpp @@ -49,7 +49,6 @@ #include #include #include -#include #include #include #include @@ -755,11 +754,11 @@ public: } // Helper function that returns the Offers on an account. - static std::vector> + static std::vector offersOnAccount(jtx::Env& env, jtx::Account const& account) { - std::vector> result; - forEachItem(*env.current(), account, [&result](std::shared_ptr const& sle) { + std::vector result; + forEachItem(*env.current(), account, [&result](SLE::const_ref sle) { if (sle->getType() == ltOFFER) result.push_back(sle); }); @@ -3928,10 +3927,10 @@ public: // clang-format off TestData const tests[]{ // btcStart --------------------- actor[0] --------------------- -------------------- actor[1] ------------------- - {.self=0, .leg0=0, .leg1=1, .btcStart=btc(20), .actors={{"ann", 0, drops(3900000'000000 - (4 * baseFee)), btc(20.0), usd(3000)}, {"abe", 0, drops(4100000'000000 - (3 * baseFee)), btc( 0), usd(750)}}}, // no BTC xfer fee - {.self=0, .leg0=1, .leg1=0, .btcStart=btc(20), .actors={{"bev", 0, drops(4100000'000000 - (4 * baseFee)), btc( 7.5), usd(2000)}, {"bob", 0, drops(3900000'000000 - (3 * baseFee)), btc(10), usd( 0)}}}, // no USD xfer fee - {.self=0, .leg0=0, .leg1=0, .btcStart=btc(20), .actors={{"cam", 0, drops(4000000'000000 - (5 * baseFee)), btc(20.0), usd(2000)} }}, // no xfer fee - {.self=0, .leg0=1, .leg1=0, .btcStart=btc( 5), .actors={{"deb", 1, drops(4040000'000000 - (4 * baseFee)), btc( 0.0), usd(2000)}, {"dan", 1, drops(3960000'000000 - (3 * baseFee)), btc( 4), usd( 0)}}}, // no USD xfer fee + {.self=0, .leg0=0, .leg1=1, .btcStart=btc(20), .actors={{.acct="ann", .offers=0, .xrp=drops(3900000'000000 - (4 * baseFee)), .btc=btc(20.0), .usd=usd(3000)}, {.acct="abe", .offers=0, .xrp=drops(4100000'000000 - (3 * baseFee)), .btc=btc( 0), .usd=usd(750)}}}, // no BTC xfer fee + {.self=0, .leg0=1, .leg1=0, .btcStart=btc(20), .actors={{.acct="bev", .offers=0, .xrp=drops(4100000'000000 - (4 * baseFee)), .btc=btc( 7.5), .usd=usd(2000)}, {.acct="bob", .offers=0, .xrp=drops(3900000'000000 - (3 * baseFee)), .btc=btc(10), .usd=usd( 0)}}}, // no USD xfer fee + {.self=0, .leg0=0, .leg1=0, .btcStart=btc(20), .actors={{.acct="cam", .offers=0, .xrp=drops(4000000'000000 - (5 * baseFee)), .btc=btc(20.0), .usd=usd(2000)} }}, // no xfer fee + {.self=0, .leg0=1, .leg1=0, .btcStart=btc( 5), .actors={{.acct="deb", .offers=1, .xrp=drops(4040000'000000 - (4 * baseFee)), .btc=btc( 0.0), .usd=usd(2000)}, {.acct="dan", .offers=1, .xrp=drops(3960000'000000 - (3 * baseFee)), .btc=btc( 4), .usd=usd( 0)}}}, // no USD xfer fee }; // clang-format on @@ -3980,7 +3979,7 @@ public: auto actorOffers = offersOnAccount(env, actor.acct); auto const offerCount = std::distance( actorOffers.begin(), - std::ranges::remove_if(actorOffers, [](std::shared_ptr& offer) { + std::ranges::remove_if(actorOffers, [](SLE::const_pointer& offer) { return (*offer)[sfTakerGets].signum() == 0; }).begin()); BEAST_EXPECT(offerCount == actor.offers); @@ -4076,8 +4075,8 @@ public: // clang-format off TestData const tests[]{ // btcStart ------------------- actor[0] -------------------- ------------------- actor[1] -------------------- - {.self=0, .leg0=0, .leg1=1, .btcStart=btc(5), .actors={{"gay", 1, drops(3950000'000000 - (4 * baseFee)), btc(5), usd(2500)}, {"gar", 1, drops(4050000'000000 - (3 * baseFee)), btc(0), usd(1375)}}}, // no BTC xfer fee - {.self=0, .leg0=0, .leg1=0, .btcStart=btc(5), .actors={{"hye", 2, drops(4000000'000000 - (5 * baseFee)), btc(5), usd(2000)} }} // no xfer fee + {.self=0, .leg0=0, .leg1=1, .btcStart=btc(5), .actors={{.acct="gay", .offers=1, .xrp=drops(3950000'000000 - (4 * baseFee)), .btc=btc(5), .usd=usd(2500)}, {.acct="gar", .offers=1, .xrp=drops(4050000'000000 - (3 * baseFee)), .btc=btc(0), .usd=usd(1375)}}}, // no BTC xfer fee + {.self=0, .leg0=0, .leg1=0, .btcStart=btc(5), .actors={{.acct="hye", .offers=2, .xrp=drops(4000000'000000 - (5 * baseFee)), .btc=btc(5), .usd=usd(2000)} }} // no xfer fee }; // clang-format on @@ -4126,7 +4125,7 @@ public: auto actorOffers = offersOnAccount(env, actor.acct); auto const offerCount = std::distance( actorOffers.begin(), - std::ranges::remove_if(actorOffers, [](std::shared_ptr& offer) { + std::ranges::remove_if(actorOffers, [](SLE::const_pointer& offer) { return (*offer)[sfTakerGets].signum() == 0; }).begin()); BEAST_EXPECT(offerCount == actor.offers); @@ -4641,7 +4640,7 @@ public: env(offer(alice, xts(30), xxx(10)), Json(jss::Flags, tfSell)); std::map> offers; - forEachItem(*env.current(), alice, [&](std::shared_ptr const& sle) { + forEachItem(*env.current(), alice, [&](SLE::const_ref sle) { if (sle->getType() == ltOFFER) { offers.emplace( @@ -4676,15 +4675,13 @@ public: } // Helper function that returns offers on an account sorted by sequence. - static std::vector> + static std::vector sortedOffersOnAccount(jtx::Env& env, jtx::Account const& acct) { - std::vector> offers{offersOnAccount(env, acct)}; - std::ranges::sort( - offers, - [](std::shared_ptr const& rhs, std::shared_ptr const& lhs) { - return (*rhs)[sfSequence] < (*lhs)[sfSequence]; - }); + std::vector offers{offersOnAccount(env, acct)}; + std::ranges::sort(offers, [](SLE::const_ref rhs, SLE::const_ref lhs) { + return (*rhs)[sfSequence] < (*lhs)[sfSequence]; + }); return offers; } diff --git a/src/test/app/PayChan_test.cpp b/src/test/app/PayChan_test.cpp index b81afa830e..0b4222ca48 100644 --- a/src/test/app/PayChan_test.cpp +++ b/src/test/app/PayChan_test.cpp @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -57,7 +56,7 @@ using namespace jtx::paychan; struct PayChan_test : public beast::unit_test::Suite { - static std::pair> + static std::pair channelKeyAndSle(ReadView const& view, jtx::Account const& account, jtx::Account const& dst) { auto const sle = view.read(keylet::account(account)); @@ -869,7 +868,7 @@ struct PayChan_test : public beast::unit_test::Suite env.close(); // Setup deposit authorization - env(deposit::authCredentials(bob, {{carol, credType}})); + env(deposit::authCredentials(bob, {{.issuer = carol, .credType = credType}})); env.close(); // Fail, credentials doesn’t belong to root account @@ -1665,9 +1664,8 @@ struct PayChan_test : public beast::unit_test::Suite auto const settleDelay = 100s; auto const pk = alice.pk(); - auto inOwnerDir = [](ReadView const& view, - Account const& acc, - std::shared_ptr const& chan) -> bool { + auto inOwnerDir = + [](ReadView const& view, Account const& acc, SLE::const_ref chan) -> bool { xrpl::Dir const ownerDir(view, keylet::ownerDir(acc.id())); // NOLINTNEXTLINE(modernize-use-ranges) return std::find(ownerDir.begin(), ownerDir.end(), chan) != ownerDir.end(); diff --git a/src/test/app/XChain_test.cpp b/src/test/app/XChain_test.cpp index 0198a36e96..de80444f2e 100644 --- a/src/test/app/XChain_test.cpp +++ b/src/test/app/XChain_test.cpp @@ -150,18 +150,18 @@ struct SEnv return env.current()->fees().base; } - std::shared_ptr + SLE::const_pointer account(jtx::Account const& account) { return env.le(account); } - std::shared_ptr + SLE::const_pointer bridge(json::Value const& jvb) { STXChainBridge const b(jvb); - auto tryGet = [&](STXChainBridge::ChainType ct) -> std::shared_ptr { + auto tryGet = [&](STXChainBridge::ChainType ct) -> SLE::const_pointer { if (auto r = env.le(keylet::bridge(b, ct))) { if ((*r)[sfXChainBridge] == b) @@ -186,13 +186,13 @@ struct SEnv return (*bridge(jvb))[sfXChainClaimID]; } - std::shared_ptr + SLE::const_pointer claimID(json::Value const& jvb, std::uint64_t seq) { return env.le(keylet::xChainClaimID(STXChainBridge(jvb), seq)); } - std::shared_ptr + SLE::const_pointer caClaimID(json::Value const& jvb, std::uint64_t seq) { return env.le(keylet::xChainCreateAccountClaimID(STXChainBridge(jvb), seq)); diff --git a/src/test/jtx/Env.h b/src/test/jtx/Env.h index 4618b36fde..3d813d993c 100644 --- a/src/test/jtx/Env.h +++ b/src/test/jtx/Env.h @@ -531,13 +531,13 @@ public: /** Return an account root. @return empty if the account does not exist. */ - [[nodiscard]] std::shared_ptr + [[nodiscard]] SLE::const_pointer le(Account const& account) const; /** Return a ledger entry. @return empty if the ledger entry does not exist */ - [[nodiscard]] std::shared_ptr + [[nodiscard]] SLE::const_pointer le(Keylet const& k) const; /** Create a JTx from parameters. */ diff --git a/src/test/jtx/PathSet.h b/src/test/jtx/PathSet.h index cab31ea540..a391adcb1b 100644 --- a/src/test/jtx/PathSet.h +++ b/src/test/jtx/PathSet.h @@ -18,7 +18,7 @@ countOffers( Asset const& takerGets) { size_t count = 0; - forEachItem(*env.current(), account, [&](std::shared_ptr const& sle) { + forEachItem(*env.current(), account, [&](SLE::const_ref sle) { if (sle->getType() == ltOFFER && sle->getFieldAmount(sfTakerPays).asset() == takerPays && sle->getFieldAmount(sfTakerGets).asset() == takerGets) ++count; @@ -34,7 +34,7 @@ countOffers( STAmount const& takerGets) { size_t count = 0; - forEachItem(*env.current(), account, [&](std::shared_ptr const& sle) { + forEachItem(*env.current(), account, [&](SLE::const_ref sle) { if (sle->getType() == ltOFFER && sle->getFieldAmount(sfTakerPays) == takerPays && sle->getFieldAmount(sfTakerGets) == takerGets) ++count; diff --git a/src/test/jtx/TestHelpers.h b/src/test/jtx/TestHelpers.h index 011ac2e58d..27c54d830b 100644 --- a/src/test/jtx/TestHelpers.h +++ b/src/test/jtx/TestHelpers.h @@ -356,7 +356,7 @@ checkVL(Slice const& result, std::string const& expected) [[nodiscard]] inline bool -checkVL(std::shared_ptr const& sle, SField const& field, std::string const& expected) +checkVL(SLE::const_ref sle, SField const& field, std::string const& expected) { return strHex(expected) == strHex(sle->getFieldVL(field)); } diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index 4b6955bafb..707e1338a7 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -280,13 +280,13 @@ Env::seq(Account const& account) const return sle->getFieldU32(sfSequence); } -std::shared_ptr +SLE::const_pointer Env::le(Account const& account) const { return le(keylet::account(account.id())); } -std::shared_ptr +SLE::const_pointer Env::le(Keylet const& k) const { return current()->read(k); diff --git a/src/test/jtx/impl/TestHelpers.cpp b/src/test/jtx/impl/TestHelpers.cpp index c784c074de..a8ec899c8a 100644 --- a/src/test/jtx/impl/TestHelpers.cpp +++ b/src/test/jtx/impl/TestHelpers.cpp @@ -402,7 +402,7 @@ expectOffers( { std::uint16_t cnt = 0; std::uint16_t matched = 0; - forEachItem(*env.current(), account, [&](std::shared_ptr const& sle) { + forEachItem(*env.current(), account, [&](SLE::const_ref sle) { if (!sle) return false; if (sle->getType() == ltOFFER) diff --git a/src/test/jtx/impl/owners.cpp b/src/test/jtx/impl/owners.cpp index cb2cd5af29..2ff93757f0 100644 --- a/src/test/jtx/impl/owners.cpp +++ b/src/test/jtx/impl/owners.cpp @@ -10,8 +10,6 @@ #include #include -#include - namespace xrpl { namespace detail { @@ -19,7 +17,7 @@ std::uint32_t ownedCountOf(ReadView const& view, AccountID const& id, LedgerEntryType type) { std::uint32_t count = 0; - forEachItem(view, id, [&count, type](std::shared_ptr const& sle) { + forEachItem(view, id, [&count, type](SLE::const_ref sle) { if (sle->getType() == type) ++count; }); diff --git a/src/test/ledger/View_test.cpp b/src/test/ledger/View_test.cpp index d1c7316588..b62061d38d 100644 --- a/src/test/ledger/View_test.cpp +++ b/src/test/ledger/View_test.cpp @@ -61,7 +61,7 @@ class View_test : public beast::unit_test::Suite } // Create SLE with key and payload - static std::shared_ptr + static SLE::pointer sle(std::uint64_t id, std::uint32_t seq = 1) { auto const le = std::make_shared(k(id)); @@ -79,7 +79,7 @@ class View_test : public beast::unit_test::Suite // Set payload on SLE static void - seq(std::shared_ptr const& le, std::uint32_t seq) + seq(SLE::ref le, std::uint32_t seq) { le->setFieldU32(sfSequence, seq); } diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index 12c79b821c..54cf85ba35 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -4316,7 +4316,7 @@ NetworkOPsImp::getBookPage( bool bDone = false; bool bDirectAdvance = true; - std::shared_ptr sleOfferDir; + SLE::const_pointer sleOfferDir; uint256 offerIndex; unsigned int uBookEntry = 0; STAmount saDirRate; diff --git a/src/xrpld/app/misc/TxQ.h b/src/xrpld/app/misc/TxQ.h index ad689abed4..d3caec55cf 100644 --- a/src/xrpld/app/misc/TxQ.h +++ b/src/xrpld/app/misc/TxQ.h @@ -288,7 +288,7 @@ public: /** Return the next sequence that would go in the TxQ for an account. */ SeqProxy - nextQueuableSeq(std::shared_ptr const& sleAccount) const; + nextQueuableSeq(SLE::const_ref sleAccount) const; /** Returns fee metrics in reference fee level units. */ @@ -342,9 +342,7 @@ public: private: // Implementation for nextQueuableSeq(). The passed lock must be held. SeqProxy - nextQueuableSeqImpl( - std::shared_ptr const& sleAccount, - std::scoped_lock const&) const; + nextQueuableSeqImpl(SLE::const_ref sleAccount, std::scoped_lock const&) const; /** Track and use the fee escalation metrics of the @@ -782,7 +780,7 @@ private: STTx const&, ApplyFlags const, OpenView const&, - std::shared_ptr const& sleAccount, + SLE::const_ref sleAccount, AccountMap::iterator const&, std::optional const&, std::scoped_lock const& lock); diff --git a/src/xrpld/app/misc/detail/TxQ.cpp b/src/xrpld/app/misc/detail/TxQ.cpp index 0f70f17046..c12632875d 100644 --- a/src/xrpld/app/misc/detail/TxQ.cpp +++ b/src/xrpld/app/misc/detail/TxQ.cpp @@ -150,7 +150,7 @@ TxQ::FeeMetrics::update( // current size limit, use a limit that is // 90% of the way from max_element to the // current size limit. - return (txnsExpected_ * 9 + *iter) / 10; + return ((txnsExpected_ * 9) + *iter) / 10; }(); // Ledgers are processing in a timely manner, // so keep the limit high, but don't let it @@ -218,7 +218,7 @@ sumOfFirstSquares(std::size_t xIn) // in a ledger, this is the least of our problems. if (x >= (1 << 21)) return {false, std::numeric_limits::max()}; - return {true, (x * (x + 1) * (2 * x + 1)) / 6}; + return {true, (x * (x + 1) * ((2 * x) + 1)) / 6}; } // Unit tests for sumOfSquares() @@ -387,7 +387,7 @@ TxQ::canBeHeld( STTx const& tx, ApplyFlags const flags, OpenView const& view, - std::shared_ptr const& sleAccount, + SLE::const_ref sleAccount, AccountMap::iterator const& accountIter, std::optional const& replacementIter, std::scoped_lock const& lock) @@ -1576,7 +1576,7 @@ TxQ::accept(Application& app, OpenView& view) // // Acquires a lock and calls the implementation. SeqProxy -TxQ::nextQueuableSeq(std::shared_ptr const& sleAccount) const +TxQ::nextQueuableSeq(SLE::const_ref sleAccount) const { std::scoped_lock const lock(mutex_); return nextQueuableSeqImpl(sleAccount, lock); @@ -1589,9 +1589,7 @@ TxQ::nextQueuableSeq(std::shared_ptr const& sleAccount) const // sequence number, that is not used by a transaction in the queue, must // be found and returned. SeqProxy -TxQ::nextQueuableSeqImpl( - std::shared_ptr const& sleAccount, - std::scoped_lock const&) const +TxQ::nextQueuableSeqImpl(SLE::const_ref sleAccount, std::scoped_lock const&) const { // If the account is not in the ledger or a non-account was passed // then return zero. We have no idea. diff --git a/src/xrpld/consensus/DisputedTx.h b/src/xrpld/consensus/DisputedTx.h index 1c85a3537d..1c0c069f54 100644 --- a/src/xrpld/consensus/DisputedTx.h +++ b/src/xrpld/consensus/DisputedTx.h @@ -286,7 +286,7 @@ DisputedTx::updateVote(int percentTime, bool proposing, ConsensusPar if (proposing) // give ourselves full weight { // This is basically the percentage of nodes voting 'yes' (including us) - weight = (yays_ * 100 + (ourVote_ ? 100 : 0)) / (nays_ + yays_ + 1); + weight = ((yays_ * 100) + (ourVote_ ? 100 : 0)) / (nays_ + yays_ + 1); newPosition = weight > requiredPct; } diff --git a/src/xrpld/rpc/detail/AssetCache.cpp b/src/xrpld/rpc/detail/AssetCache.cpp index a0743a2303..0976290d4c 100644 --- a/src/xrpld/rpc/detail/AssetCache.cpp +++ b/src/xrpld/rpc/detail/AssetCache.cpp @@ -124,7 +124,7 @@ AssetCache::getMPTs(xrpl::AccountID const& account) std::vector mpts; // Get issued/authorized tokens - forEachItem(*ledger_, account, [&](std::shared_ptr const& sle) { + forEachItem(*ledger_, account, [&](SLE::const_ref sle) { if (sle->getType() == ltMPTOKEN_ISSUANCE) { auto const mptID = makeMptID(sle->getFieldU32(sfSequence), account); diff --git a/src/xrpld/rpc/detail/RPCHelpers.cpp b/src/xrpld/rpc/detail/RPCHelpers.cpp index d48057a0a8..7ab2468b75 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.cpp +++ b/src/xrpld/rpc/detail/RPCHelpers.cpp @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -46,7 +45,7 @@ namespace xrpl::RPC { std::uint64_t -getStartHint(std::shared_ptr const& sle, AccountID const& accountID) +getStartHint(SLE::const_ref sle, AccountID const& accountID) { if (sle->getType() == ltRIPPLE_STATE) { @@ -67,10 +66,7 @@ getStartHint(std::shared_ptr const& sle, AccountID const& accountID) } bool -isRelatedToAccount( - ReadView const& ledger, - std::shared_ptr const& sle, - AccountID const& accountID) +isRelatedToAccount(ReadView const& ledger, SLE::const_ref sle, AccountID const& accountID) { if (sle->getType() == ltRIPPLE_STATE) { diff --git a/src/xrpld/rpc/detail/RPCHelpers.h b/src/xrpld/rpc/detail/RPCHelpers.h index 781db1b8a5..bbc101a072 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.h +++ b/src/xrpld/rpc/detail/RPCHelpers.h @@ -33,7 +33,7 @@ struct JsonContext; * @return A 64-bit unsigned integer representing the start hint for traversal. */ std::uint64_t -getStartHint(std::shared_ptr const& sle, AccountID const& accountID); +getStartHint(SLE::const_ref sle, AccountID const& accountID); /** * @brief Tests if a ledger entry (SLE) is owned by the specified account. @@ -47,10 +47,7 @@ getStartHint(std::shared_ptr const& sle, AccountID const& accountID); * @return true if the SLE is owned by the account, false otherwise. */ bool -isRelatedToAccount( - ReadView const& ledger, - std::shared_ptr const& sle, - AccountID const& accountID); +isRelatedToAccount(ReadView const& ledger, SLE::const_ref sle, AccountID const& accountID); /** * @brief Parses an array of account IDs from a JSON value. diff --git a/src/xrpld/rpc/detail/TransactionSign.cpp b/src/xrpld/rpc/detail/TransactionSign.cpp index dd0e78c178..86d895fa1b 100644 --- a/src/xrpld/rpc/detail/TransactionSign.cpp +++ b/src/xrpld/rpc/detail/TransactionSign.cpp @@ -165,7 +165,7 @@ public: static ErrorCodeI acctMatchesPubKey( - std::shared_ptr accountState, + SLE::const_pointer accountState, AccountID const& accountID, PublicKey const& publicKey) { @@ -519,7 +519,7 @@ transactionPreProcessImpl( if (!verify && !txJson.isMember(jss::Sequence)) return RPC::missingFieldError("tx_json.Sequence"); - std::shared_ptr sle; + SLE::const_pointer sle; if (verify) sle = app.getOpenLedger().current()->read(keylet::account(srcAddressID)); @@ -1222,8 +1222,7 @@ transactionSignFor( signForParams.validMultiSign(), "xrpl::RPC::transactionSignFor : valid multi-signature"); { - std::shared_ptr const accountState = - ledger->read(keylet::account(*signerAccountID)); + SLE::const_pointer const accountState = ledger->read(keylet::account(*signerAccountID)); // Make sure the account and secret belong together. auto const err = acctMatchesPubKey(accountState, *signerAccountID, signForParams.getPublicKey()); @@ -1310,7 +1309,7 @@ transactionSubmitMultiSigned( if (RPC::containsError(txJsonResult)) return std::move(txJsonResult); - std::shared_ptr const sle = ledger->read(keylet::account(srcAddressID)); + SLE::const_pointer const sle = ledger->read(keylet::account(srcAddressID)); if (!sle) { diff --git a/src/xrpld/rpc/detail/TrustLine.cpp b/src/xrpld/rpc/detail/TrustLine.cpp index f7293d0816..77a2b36d56 100644 --- a/src/xrpld/rpc/detail/TrustLine.cpp +++ b/src/xrpld/rpc/detail/TrustLine.cpp @@ -9,13 +9,12 @@ #include #include -#include #include #include namespace xrpl { -TrustLineBase::TrustLineBase(std::shared_ptr const& sle, AccountID const& viewAccount) +TrustLineBase::TrustLineBase(SLE::const_ref sle, AccountID const& viewAccount) : key_(sle->key()) , lowLimit_(sle->getFieldAmount(sfLowLimit)) , highLimit_(sle->getFieldAmount(sfHighLimit)) @@ -37,7 +36,7 @@ TrustLineBase::getJson(int) } std::optional -PathFindTrustLine::makeItem(AccountID const& accountID, std::shared_ptr const& sle) +PathFindTrustLine::makeItem(AccountID const& accountID, SLE::const_ref sle) { if (!sle || sle->getType() != ltRIPPLE_STATE) return {}; @@ -53,14 +52,11 @@ getTrustLineItems( LineDirection direction = LineDirection::Outgoing) { std::vector items; - forEachItem( - view, - accountID, - [&items, &accountID, &direction](std::shared_ptr const& sleCur) { - auto ret = T::makeItem(accountID, sleCur); - if (ret && (direction == LineDirection::Outgoing || !ret->getNoRipple())) - items.push_back(std::move(*ret)); - }); + forEachItem(view, accountID, [&items, &accountID, &direction](SLE::const_ref sleCur) { + auto ret = T::makeItem(accountID, sleCur); + if (ret && (direction == LineDirection::Outgoing || !ret->getNoRipple())) + items.push_back(std::move(*ret)); + }); // This list may be around for a while, so free up any unneeded // capacity items.shrink_to_fit(); @@ -78,7 +74,7 @@ PathFindTrustLine::getItems( return detail::getTrustLineItems(accountID, view, direction); } -RPCTrustLine::RPCTrustLine(std::shared_ptr const& sle, AccountID const& viewAccount) +RPCTrustLine::RPCTrustLine(SLE::const_ref sle, AccountID const& viewAccount) : TrustLineBase(sle, viewAccount) , lowQualityIn_(sle->getFieldU32(sfLowQualityIn)) , lowQualityOut_(sle->getFieldU32(sfLowQualityOut)) @@ -88,7 +84,7 @@ RPCTrustLine::RPCTrustLine(std::shared_ptr const& sle, AccountID cons } std::optional -RPCTrustLine::makeItem(AccountID const& accountID, std::shared_ptr const& sle) +RPCTrustLine::makeItem(AccountID const& accountID, SLE::const_ref sle) { if (!sle || sle->getType() != ltRIPPLE_STATE) return {}; diff --git a/src/xrpld/rpc/detail/TrustLine.h b/src/xrpld/rpc/detail/TrustLine.h index 72d4d44ae3..7a0a01d744 100644 --- a/src/xrpld/rpc/detail/TrustLine.h +++ b/src/xrpld/rpc/detail/TrustLine.h @@ -39,7 +39,7 @@ public: protected: // This class should not be instantiated directly. Use one of the derived // classes. - TrustLineBase(std::shared_ptr const& sle, AccountID const& viewAccount); + TrustLineBase(SLE::const_ref sle, AccountID const& viewAccount); ~TrustLineBase() = default; TrustLineBase(TrustLineBase const&) = default; @@ -175,7 +175,7 @@ public: PathFindTrustLine() = delete; static std::optional - makeItem(AccountID const& accountID, std::shared_ptr const& sle); + makeItem(AccountID const& accountID, SLE::const_ref sle); static std::vector getItems(AccountID const& accountID, ReadView const& view, LineDirection direction); @@ -190,7 +190,7 @@ class RPCTrustLine final : public TrustLineBase, public CountedObject const& sle, AccountID const& viewAccount); + RPCTrustLine(SLE::const_ref sle, AccountID const& viewAccount); [[nodiscard]] Rate const& getQualityIn() const @@ -205,7 +205,7 @@ public: } static std::optional - makeItem(AccountID const& accountID, std::shared_ptr const& sle); + makeItem(AccountID const& accountID, SLE::const_ref sle); static std::vector getItems(AccountID const& accountID, ReadView const& view); diff --git a/src/xrpld/rpc/handlers/account/AccountChannels.cpp b/src/xrpld/rpc/handlers/account/AccountChannels.cpp index 6d5876322c..8a5c7dc6e3 100644 --- a/src/xrpld/rpc/handlers/account/AccountChannels.cpp +++ b/src/xrpld/rpc/handlers/account/AccountChannels.cpp @@ -114,7 +114,7 @@ doAccountChannels(RPC::JsonContext& context) json::Value jsonChannels{json::ValueType::Array}; struct VisitData { - std::vector> items; + std::vector items; AccountID const& accountID; std::optional const& raDstAccount; }; @@ -170,8 +170,7 @@ doAccountChannels(RPC::JsonContext& context) startAfter, startHint, limit + 1, - [&visitData, &accountID, &count, &limit, &marker, &nextHint]( - std::shared_ptr const& sleCur) { + [&visitData, &accountID, &count, &limit, &marker, &nextHint](SLE::const_ref sleCur) { if (!sleCur) { // LCOV_EXCL_START diff --git a/src/xrpld/rpc/handlers/account/AccountLines.cpp b/src/xrpld/rpc/handlers/account/AccountLines.cpp index c60ce90201..e69f70ca5a 100644 --- a/src/xrpld/rpc/handlers/account/AccountLines.cpp +++ b/src/xrpld/rpc/handlers/account/AccountLines.cpp @@ -195,8 +195,7 @@ doAccountLines(RPC::JsonContext& context) startAfter, startHint, limit + 1, - [&visitData, &count, &marker, &limit, &nextHint]( - std::shared_ptr const& sleCur) { + [&visitData, &count, &marker, &limit, &nextHint](SLE::const_ref sleCur) { if (!sleCur) { // LCOV_EXCL_START diff --git a/src/xrpld/rpc/handlers/account/AccountOffers.cpp b/src/xrpld/rpc/handlers/account/AccountOffers.cpp index 85d9470b75..4829ff56b1 100644 --- a/src/xrpld/rpc/handlers/account/AccountOffers.cpp +++ b/src/xrpld/rpc/handlers/account/AccountOffers.cpp @@ -33,7 +33,7 @@ namespace xrpl { void -appendOfferJson(std::shared_ptr const& offer, json::Value& offers) +appendOfferJson(SLE::const_ref offer, json::Value& offers) { STAmount const dirRate = amountFromQuality(getQuality(offer->getFieldH256(sfBookDirectory))); json::Value& obj(offers.append(json::ValueType::Object)); @@ -87,7 +87,7 @@ doAccountOffers(RPC::JsonContext& context) return *err; json::Value& jsonOffers(result[jss::offers] = json::ValueType::Array); - std::vector> offers; + std::vector offers; uint256 startAfter = beast::kZero; std::uint64_t startHint = 0; @@ -138,8 +138,7 @@ doAccountOffers(RPC::JsonContext& context) startAfter, startHint, limit + 1, - [&offers, &count, &marker, &limit, &nextHint, &accountID]( - std::shared_ptr const& sle) { + [&offers, &count, &marker, &limit, &nextHint, &accountID](SLE::const_ref sle) { if (!sle) { // LCOV_EXCL_START diff --git a/src/xrpld/rpc/handlers/account/GatewayBalances.cpp b/src/xrpld/rpc/handlers/account/GatewayBalances.cpp index 146b9ead5c..bd1681172c 100644 --- a/src/xrpld/rpc/handlers/account/GatewayBalances.cpp +++ b/src/xrpld/rpc/handlers/account/GatewayBalances.cpp @@ -144,7 +144,7 @@ doGatewayBalances(RPC::JsonContext& context) // Traverse the cold wallet's trust lines { - forEachItem(*ledger, accountID, [&](std::shared_ptr const& sle) { + forEachItem(*ledger, accountID, [&](SLE::const_ref sle) { if (sle->getType() == ltESCROW) { auto const& escrow = sle->getFieldAmount(sfAmount); diff --git a/src/xrpld/rpc/handlers/account/NoRippleCheck.cpp b/src/xrpld/rpc/handlers/account/NoRippleCheck.cpp index bb48d3ebd5..d8bb65aba9 100644 --- a/src/xrpld/rpc/handlers/account/NoRippleCheck.cpp +++ b/src/xrpld/rpc/handlers/account/NoRippleCheck.cpp @@ -137,52 +137,50 @@ doNoRippleCheck(RPC::JsonContext& context) } } - forEachItemAfter( - *ledger, accountID, uint256(), 0, limit, [&](std::shared_ptr const& ownedItem) { - if (ownedItem->getType() == ltRIPPLE_STATE) + forEachItemAfter(*ledger, accountID, uint256(), 0, limit, [&](SLE::const_ref ownedItem) { + if (ownedItem->getType() == ltRIPPLE_STATE) + { + bool const bLow = accountID == ownedItem->getFieldAmount(sfLowLimit).getIssuer(); + + bool const bNoRipple = ownedItem->isFlag(bLow ? lsfLowNoRipple : lsfHighNoRipple); + + std::string problem; + bool needFix = false; + if (bNoRipple && roleGateway) { - bool const bLow = accountID == ownedItem->getFieldAmount(sfLowLimit).getIssuer(); - - bool const bNoRipple = ownedItem->isFlag(bLow ? lsfLowNoRipple : lsfHighNoRipple); - - std::string problem; - bool needFix = false; - if (bNoRipple && roleGateway) - { - problem = "You should clear the no ripple flag on your "; - needFix = true; - } - else if (!roleGateway && !bNoRipple) - { - problem = "You should probably set the no ripple flag on your "; - needFix = true; - } - if (needFix) - { - AccountID const peer = - ownedItem->getFieldAmount(bLow ? sfHighLimit : sfLowLimit).getIssuer(); - STAmount const peerLimit = - ownedItem->getFieldAmount(bLow ? sfHighLimit : sfLowLimit); - problem += to_string(peerLimit.get().currency); - problem += " line to "; - problem += to_string(peerLimit.getIssuer()); - problems.append(problem); - - STAmount limitAmount( - ownedItem->getFieldAmount(bLow ? sfLowLimit : sfHighLimit)); - limitAmount.get().account = peer; - - json::Value& tx = jvTransactions.append(json::ValueType::Object); - tx["TransactionType"] = jss::TrustSet; - tx["LimitAmount"] = limitAmount.getJson(JsonOptions::Values::None); - tx["Flags"] = bNoRipple ? tfClearNoRipple : tfSetNoRipple; - fillTransaction(context, tx, accountID, seq, *ledger); - - return true; - } + problem = "You should clear the no ripple flag on your "; + needFix = true; } - return false; - }); + else if (!roleGateway && !bNoRipple) + { + problem = "You should probably set the no ripple flag on your "; + needFix = true; + } + if (needFix) + { + AccountID const peer = + ownedItem->getFieldAmount(bLow ? sfHighLimit : sfLowLimit).getIssuer(); + STAmount const peerLimit = + ownedItem->getFieldAmount(bLow ? sfHighLimit : sfLowLimit); + problem += to_string(peerLimit.get().currency); + problem += " line to "; + problem += to_string(peerLimit.getIssuer()); + problems.append(problem); + + STAmount limitAmount(ownedItem->getFieldAmount(bLow ? sfLowLimit : sfHighLimit)); + limitAmount.get().account = peer; + + json::Value& tx = jvTransactions.append(json::ValueType::Object); + tx["TransactionType"] = jss::TrustSet; + tx["LimitAmount"] = limitAmount.getJson(JsonOptions::Values::None); + tx["Flags"] = bNoRipple ? tfClearNoRipple : tfSetNoRipple; + fillTransaction(context, tx, accountID, seq, *ledger); + + return true; + } + } + return false; + }); return result; } diff --git a/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp b/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp index df6772e4c0..b9f4a42880 100644 --- a/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp +++ b/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp @@ -76,7 +76,7 @@ doAMMInfo(RPC::JsonContext& context) std::optional accountID; Asset asset1; Asset asset2; - std::shared_ptr amm; + SLE::const_pointer amm; }; auto getValuesFromContextParams = [&]() -> Expected { diff --git a/src/xrpld/rpc/handlers/orderbook/DepositAuthorized.cpp b/src/xrpld/rpc/handlers/orderbook/DepositAuthorized.cpp index cc176aaadb..343d539277 100644 --- a/src/xrpld/rpc/handlers/orderbook/DepositAuthorized.cpp +++ b/src/xrpld/rpc/handlers/orderbook/DepositAuthorized.cpp @@ -90,7 +90,7 @@ doDepositAuthorized(RPC::JsonContext& context) bool const credentialsPresent = params.isMember(jss::credentials); std::set> sorted; - std::vector> lifeExtender; + std::vector lifeExtender; if (credentialsPresent) { auto const& creds(params[jss::credentials]); @@ -128,7 +128,7 @@ doDepositAuthorized(RPC::JsonContext& context) jss::credentials, "an array of CredentialID(hash256)")); } - std::shared_ptr sleCred = ledger->read(keylet::credential(credH)); + SLE::const_pointer sleCred = ledger->read(keylet::credential(credH)); if (!sleCred) { RPC::injectError(RpcBadCredentials, "credentials don't exist", result); diff --git a/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp b/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp index ae551de1ab..6a75277b1b 100644 --- a/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp +++ b/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp @@ -48,7 +48,7 @@ using Prices = static void iteratePriceData( RPC::JsonContext& context, - std::shared_ptr const& sle, + SLE::const_ref sle, std::function const& f) { static constexpr std::uint8_t kMaxHistory = 3; diff --git a/src/xrpld/rpc/handlers/orderbook/NFTOffersHelpers.h b/src/xrpld/rpc/handlers/orderbook/NFTOffersHelpers.h index b94e431117..8529ec2d2c 100644 --- a/src/xrpld/rpc/handlers/orderbook/NFTOffersHelpers.h +++ b/src/xrpld/rpc/handlers/orderbook/NFTOffersHelpers.h @@ -17,10 +17,7 @@ namespace xrpl { inline void -appendNftOfferJson( - Application const& app, - std::shared_ptr const& offer, - json::Value& offers) +appendNftOfferJson(Application const& app, SLE::const_ref offer, json::Value& offers) { json::Value& obj(offers.append(json::ValueType::Object)); @@ -64,7 +61,7 @@ enumerateNFTOffers(RPC::JsonContext& context, uint256 const& nftId, Keylet const json::Value& jsonOffers(result[jss::offers] = json::ValueType::Array); - std::vector> offers; + std::vector offers; unsigned int reserve(limit); uint256 startAfter; std::uint64_t startHint = 0; @@ -97,12 +94,7 @@ enumerateNFTOffers(RPC::JsonContext& context, uint256 const& nftId, Keylet const } if (!forEachItemAfter( - *ledger, - directory, - startAfter, - startHint, - reserve, - [&offers](std::shared_ptr const& offer) { + *ledger, directory, startAfter, startHint, reserve, [&offers](SLE::const_ref offer) { if (offer->getType() == ltNFTOKEN_OFFER) { offers.emplace_back(offer); diff --git a/src/xrpld/rpc/handlers/transaction/Simulate.cpp b/src/xrpld/rpc/handlers/transaction/Simulate.cpp index 7a11b728ce..676f0318a2 100644 --- a/src/xrpld/rpc/handlers/transaction/Simulate.cpp +++ b/src/xrpld/rpc/handlers/transaction/Simulate.cpp @@ -61,7 +61,7 @@ getAutofillSequence(json::Value const& txJson, RPC::JsonContext& context) return Unexpected( RPC::makeError(RpcSrcActMalformed, RPC::invalidFieldMessage("tx.Account"))); } - std::shared_ptr const sle = + SLE::const_pointer const sle = context.app.getOpenLedger().current()->read(keylet::account(*srcAddressID)); if (!hasTicketSeq && !sle) { From e209ee537167ef23f2889eec66640ef21e7ab0ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 11:29:12 -0400 Subject: [PATCH 28/28] ci: [DEPENDABOT] bump eps1lon/actions-label-merge-conflict from 3.0.3 to 3.1.0 (#7375) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/conflicting-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/conflicting-pr.yml b/.github/workflows/conflicting-pr.yml index 6e667632a0..772d46fd7d 100644 --- a/.github/workflows/conflicting-pr.yml +++ b/.github/workflows/conflicting-pr.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check if PRs are dirty - uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3 + uses: eps1lon/actions-label-merge-conflict@0273be72a0bbd58fcd71d0d6c02c209b50d1e5e1 # v3.1.0 with: dirtyLabel: "PR: has conflicts" repoToken: "${{ secrets.GITHUB_TOKEN }}"