> loading_
# Walkthrough: The three confidential MPT hardening patches
# ============================================================
# --- FIX 1: Invariant conservation check for ConfidentialMPTSend ---
# File: src/libxrpl/tx/invariants/MPTInvariant.cpp
# In ValidConfidentialMPToken::finalize, the existing logic only
# checks conservation when coaDelta != 0. For ConfidentialMPTSend,
# coaDelta is always 0 — meaning NO invariant fires at all.
# Before (simplified):
# if (coaDelta != 0) {
# // ... conservation checks ...
# }
# // ConfidentialMPTSend falls through with no check
# After — add an explicit branch for Send operations:
# if (tx.getTxnType() == ttCONFIDENTIAL_MPT_SEND && coaDelta == 0) {
# // Public fields must not change during a confidential send
# if (mptAmountDelta != 0) {
# JLOG(j.fatal()) << "Invariant: MPTAmount changed during ConfidentialMPTSend";
# return false;
# }
# if (outstandingDelta != 0) {
# JLOG(j.fatal()) << "Invariant: OutstandingAmount changed during ConfidentialMPTSend";
# return false;
# }
# }
# Unit test (src/test/app/Invariant_test.cpp):
# Simulate a ConfidentialMPTSend, then manually mutate
# sfMPTAmount on the resulting SLE before invariant check.
# Expect the invariant to fire and the transaction to fail.
# env(confSend(alice, bob, mpt(100)), ter(tecINVARIANT_FAILED));
# --- FIX 2: checkExtraFeatures for sfCredentialIDs ---
# File: src/libxrpl/tx/transactors/token/ConfidentialMPTSend.h
# Add the override declaration:
# NotTEC checkExtraFeatures(PreclaimContext const& ctx) const override;
# File: src/libxrpl/tx/transactors/token/ConfidentialMPTSend.cpp
# Implement following the Payment/EscrowFinish pattern:
#
# NotTEC
# ConfidentialMPTSend::checkExtraFeatures(PreclaimContext const& ctx) const
# {
# if (ctx.tx.isFieldPresent(sfCredentialIDs) &&
# !ctx.view.rules().enabled(featureCredentials))
# {
# return temDISABLED;
# }
# return tesSUCCESS;
# }
#
# This ensures sfCredentialIDs is rejected if featureCredentials
# hasn't activated yet, even if featureConfidentialTransfer has.
# --- FIX 3: Preclaim bounds check in ConfidentialMPTClawback ---
# File: src/libxrpl/tx/transactors/token/ConfidentialMPTClawback.cpp
# Around line 89-91 in preclaim(), after validating vs COA:
# // Existing check:
# auto const clawAmount = ctx.tx[sfAmount].mpt().mpt();
# if (clawAmount > (*sleIssuance)[sfConfidentialOutstandingAmount])
# return tecINSUFFICIENT_FUNDS;
# // NEW: Also validate against public OutstandingAmount
# // This prevents uint64 underflow in doApply (lines 159-161)
# // if COA and OA have diverged due to a separate bug.
# if (clawAmount > (*sleIssuance)[sfOutstandingAmount])
# {
# JLOG(ctx.j.warn())
# << "ConfidentialMPTClawback: clawAmount exceeds OutstandingAmount";
# return tecINSUFFICIENT_FUNDS;
# }
# Unit test: Create an issuance where COA = 1000 but OA = 500
# (requires direct SLE manipulation in test harness).
# Attempt clawback of 750. Should fail at preclaim with
# tecINSUFFICIENT_FUNDS rather than wrapping OA to uint64_max.
# env(confClawback(issuer, holder, mpt(750)), ter(tecINSUFFICIENT_FUNDS));