Skip to content

fix: Enforce MPT balance invariants under fixCleanup3_4_0 - #7889

Open
gregtatcam wants to merge 1 commit into
developfrom
gregtatcam/mpt/mpt-invariants-gate-update
Open

fix: Enforce MPT balance invariants under fixCleanup3_4_0#7889
gregtatcam wants to merge 1 commit into
developfrom
gregtatcam/mpt/mpt-invariants-gate-update

Conversation

@gregtatcam

Copy link
Copy Markdown
Contributor

High Level Overview of Change

Makes the ValidMPTBalanceChanges and ValidMPTTransfer invariant checks
enforce under the fixCleanup3_4_0 amendment, and extends both to hold even
when a transaction fails. Previously these invariants only enforced under
featureMPTokensV2; because that amendment is unshipped, they were effectively
advisory (log-only). This PR makes them active protections on the next cleanup
amendment.

Context of Change

The MPT balance invariants (ValidMPTBalanceChanges, ValidMPTTransfer) were
gated solely on featureMPTokensV2. Since featureMPTokensV2 is not yet
supported/shipped, the checks logged but never escalated to
tecINVARIANT_FAILED / tefINVARIANT_FAILED, so they provided no real
protection on the live network. This change adds fixCleanup3_4_0 to the gate
so enforcement turns on with the next cleanup amendment, independently of the
MPTokensV2 rollout. When both amendments are disabled the checks remain log-only
to preserve consensus with older nodes.

Separately, the invariants only ran on the success path. Per the requirement
that invariants must hold even for failed transactions, both checks now also
verify that a non-success result did not move OutstandingAmount or any
holder's MPT balance. tecINCOMPLETE and tecKILLED are exempt because some
transactors legitimately commit MPT changes while returning those codes
(AMMWithdraw commits the pool on tecINCOMPLETE; fill-or-kill OfferCreate
and lending can return tecKILLED after applying MPT changes). Any new
transactor that persists MPT state on a non-tesSUCCESS result must be reviewed
against these checks.

API Impact

  • Public API: New feature (new methods and/or new fields)
  • Public API: Breaking change (in general, breaking changes should only impact the next api_version)
  • libxrpl change (any change that may affect libxrpl or dependents of libxrpl)
  • Peer protocol change (must be backward compatible or bump the peer protocol version)

No public API, libxrpl, or peer protocol impact. The behavior change is gated
behind the fixCleanup3_4_0 amendment.

Test Plan

Invariants_test was updated and run to cover the new behavior:

  • doInvariantCheck gains an initialResult seed so a check can be exercised
    starting from a non-tesSUCCESS input, and it now asserts only when an
    invariant actually fires on a pass (terActual != terInput).
  • Expectations that relied on the old success-only skip were updated: a failing
    transaction that trips an invariant now escalates to tefINVARIANT_FAILED on
    the second pass (rather than staying tecINVARIANT_FAILED).
  • Added regression coverage for the tecINCOMPLETE / tecKILLED exemptions
    (invariant must not fire) and for the on-failure balance/transfer checks
    (invariant must fire on other failure codes such as tecEXPIRED).

@gregtatcam gregtatcam changed the title Enforce MPT balance invariants under fixCleanup3_4_0 fix: Enforce MPT balance invariants under fixCleanup3_4_0 Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant