Skip to content

feat: Support ConfidentialMPTMirrorUpdate - #8192

Open
yinyiqian1 wants to merge 13 commits into
developfrom
yinyi/mirror-update
Open

yinyiqian1 wants to merge 13 commits into
developfrom
yinyi/mirror-update

Conversation

@yinyiqian1

@yinyiqian1 yinyiqian1 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

High Level Overview of Change

Adds the ConfidentialMPTMirrorUpdate transaction (type 92, gated on featureConfidentialMPTKeyRotation),
which re-encrypts a holder's mirrored confidential balances under the ElGamal public keys currently registered
on the MPTokenIssuance.

Transaction shape

Field Presence Notes
MPTokenIssuanceID required
Holder optional Presence selects the mode: present = issuer mode, absent = holder self-migration
IssuerEncryptedAmount optional The full re-encrypted issuer mirror
AuditorEncryptedAmount optional The full re-encrypted auditor mirror
ZKProof required Reserved; verification lands in a follow-up PR
At least one of IssuerEncryptedAmount / AuditorEncryptedAmount must be present.

Supported flows

Issuer mode, submitted by the issuer with Holder field:

  1. Issuer key rotation migration — re-encrypts the holder's IssuerEncryptedBalance under the issuer's new key.
  2. Auditor key rotation migration — re-encrypts the holder's AuditorEncryptedBalance under the auditor's new key.
  3. Simultaneous rotation migration — updates both mirrors in one transaction, when both keys have rotated.
  4. Auditor late-registration migration — when an auditor key is registered through MPTokenIssuanceSet after
    holders already hold confidential balances, this sets each holder's initial AuditorEncryptedBalance.
    Holder self-migration, submitted by the holder with Holder field absent. The holder decrypts their own
    ConfidentialBalanceSpending and re-encrypts it under the relevant new key(s):
  5. Holder issuer-mirror migration — re-encrypts the holder's IssuerEncryptedBalance under the issuer's new key.
  6. Holder auditor-mirror migration — re-encrypts the holder's AuditorEncryptedBalance under the auditor's new key,
    or sets it for the first time when the auditor key was registered after the holder already held a confidential balance.
  7. Simultaneous holder self-migration — updates both mirrors in one transaction, when both keys have rotated.

Please Note: The zkproof related logic is not included in this PR. It will be added in a separate PR. It is safe now
since the amendment is not enabled. We are still working in progress.

Context of Change

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)

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

This PR has conflicts, please resolve them in order for the PR to be reviewed.

@github-actions

Copy link
Copy Markdown

All conflicts have been resolved. Assigned reviewers can now start or resume their review.

@yinyiqian1
yinyiqian1 marked this pull request as ready for review September 15, 2026 20:29
@yinyiqian1 yinyiqian1 changed the title feat: support ConfidentialMPTMirrorUpdate feat: Support ConfidentialMPTMirrorUpdate Sep 15, 2026
@codecov

codecov Bot commented Sep 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ConfidentialMPTMirrorUpdate transactor's preflight/preclaim logic for mode detection, staleness checks, and epoch bookkeeping is well thought out and heavily tested. The one real gap is that ZK-proof verification is explicitly stubbed out ("todo: check zkproof") in both preflight and preclaim, meaning the currently-shipped implementation never verifies the equality proof that is supposed to guarantee the new ciphertext encodes the same balance as the mirror it replaces — anyone satisfying the mode/staleness checks can currently set an arbitrary encrypted balance. The rest of the diff (mirror-epoch split in ConfidentialTransfer.cpp, new Protocol.h constant, macro/autogen wiring, test helpers) looks consistent with existing patterns.

Comment thread src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Equality-proof verification is not implemented, and proof-length validation and required API documentation updates remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds ConfidentialMPTMirrorUpdate support for confidential MPT key rotation, including transaction processing, generated APIs, helpers, and tests.

Changes:

  • Registers the transaction and generated wrappers.
  • Implements mirror updates, epoch handling, validation, and fees.
  • Adds JTX helpers and protocol/application tests.
File summaries
File Description
src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTMirrorUpdateTests.cpp Tests generated transaction APIs.
src/test/jtx/mpt.h Adds mirror-update test arguments and APIs.
src/test/jtx/impl/utility.cpp Applies confidential transaction fees.
src/test/jtx/impl/mpt.cpp Builds mirror-update test transactions.
src/test/app/ConfidentialMPTKeyRotation_test.cpp Adds key-rotation integration coverage.
src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp Implements transaction validation and mirror updates.
src/libxrpl/protocol/ConfidentialTransfer.cpp Implements mirror epoch helpers.
include/xrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.h Declares the transactor.
include/xrpl/protocol/Protocol.h Defines the equality-proof length constant.
include/xrpl/protocol/detail/transactions.macro Registers the transaction and fields.
include/xrpl/protocol/ConfidentialTransfer.h Declares mirror epoch helpers.
include/xrpl/protocol_autogen/transactions/ConfidentialMPTMirrorUpdate.h Provides generated transaction wrappers and builder.
Review details

Suppressed comments (3)

include/xrpl/protocol/detail/transactions.macro:1147

  • This PR adds a new libxrpl transaction type and protocol fields, but the PR description leaves both the libxrpl and public-API impact boxes unchecked (and no API changelog entry is included). Please update the API-impact checklist and the required changelog/documentation for this externally visible transaction.
TRANSACTION(ttCONFIDENTIAL_MPT_MIRROR_UPDATE, 92, ConfidentialMPTMirrorUpdate,
    ({.delegable = Delegation::Delegable, .amendment = featureConfidentialMPTKeyRotation}),
    ({
    {sfMPTokenIssuanceID, SoeRequired},
    {sfHolder, SoeOptional},
    {sfIssuerEncryptedAmount, SoeOptional},
    {sfAuditorEncryptedAmount, SoeOptional},
    {sfZKProof, SoeRequired},

src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp:80

  • The required sfZKProof is accepted without any size check, so malformed transactions can pass preflight with a proof of arbitrary length. Validate it against kEcEqualityProofLength here (before the ciphertext format checks or alongside them) and return temMALFORMED when it does not match.
    // todo: check zkproof

src/test/app/ConfidentialMPTKeyRotation_test.cpp:1386

  • This newly added comment has subject–verb agreement error: “issuance” is singular, so “have” should be “has.”
        // The issuance have not enabled confidential balances.
  • Files reviewed: 12/12 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp Outdated

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adds the ConfidentialMPTMirrorUpdate transaction (issuer/holder key-rotation mirror migration) plus supporting protocol plumbing and tests. The core mode/holder/staleness validation logic in preclaim looks sound and well-covered by tests. The main gap is that the new sfZKProof field is required by the schema but is never actually validated: no length check (despite a new kEcEqualityProofLength constant being introduced specifically for it) and no cryptographic verification, so any content (or garbage) currently passes. The ConfidentialTransfer.h/.cpp refactor splitting setMirrorEpochs into setIssuerMirrorEpoch/setAuditorMirrorEpoch is a clean, behavior-preserving extraction.

Comment thread src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp Outdated
Comment thread src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing ZKProof cryptographic validation—flagged inline.

Comment thread src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp
Comment thread src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical compilation and security findings remain, along with missing validation and required coverage/documentation.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (4)

Previously missed (2) — in code that hasn't changed since the last review.

src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp:78

  • kEcEqualityProofLength is introduced for this transaction, but preflight never checks sfZKProof against it. As a result, a present proof of any length (including an empty VL) is accepted and the mirror update can be applied, unlike the other confidential MPT transactions that reject malformed proof lengths. Add the fixed-length check now even if cryptographic verification is deferred.
    include/xrpl/protocol/detail/transactions.macro:1147
  • This adds a new public transaction type and fields, but the required API changelog entry is missing. Prior public transaction additions are documented in API-CHANGELOG.md (for example, lines 241–251); without an entry, clients and release documentation will not discover this amendment-gated transaction.

src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp:87

  • The shared confidential-transaction fee test enumerates the existing confidential MPT transactions but does not submit ConfidentialMPTMirrorUpdate (src/test/app/ConfidentialTransfer_test.cpp:7081-7125). The new calculateBaseFee path is therefore untested; add a successful mirror-update case that verifies the confidential fee multiplier.
XRPAmount
ConfidentialMPTMirrorUpdate::calculateBaseFee(ReadView const& view, STTx const& tx)
{
    return Transactor::calculateBaseFee(view, tx, kConfidentialFeeMultiplier);
}

src/test/app/Delegate_test.cpp:2759

  • This count update adds the new transaction to the delegable set without adding delegated test cases for it. The warning above requires every new delegable transaction's standard scenarios to be duplicated in a delegated context, but MPTMirrorUpdate has no delegate argument and the new key-rotation suite contains no delegated issuer/holder flows. Raising the count alone suppresses that security check; add those cases before changing the expected count, or do not mark the transaction delegable.
        std::size_t const expectedDelegableCount = 57;
  • Files reviewed: 13/13 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment thread src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp
Comment thread src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp
Comment thread include/xrpl/protocol/detail/transactions.macro
Comment thread src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp Outdated
Comment thread src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp Outdated
Comment thread src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp
(*sleMptoken)[sfAuditorEncryptedBalance] = ctx_.tx[sfAuditorEncryptedAmount];
setAuditorMirrorEpoch(*sleIssuance, *sleMptoken);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed sfIssuerMirrorEncryptionKey update

@yinyiqian1 yinyiqian1 Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be in a separate PR tracked in another ticket.
(That PR should initialize and update the sfIssuerMirrorEncryptionKey and sfInitialIssuerEncryptionKey)

Comment thread src/libxrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.cpp Outdated
Comment thread src/test/jtx/mpt.h
*/
struct MPTMirrorUpdate
{
std::optional<Account> account = std::nullopt;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::nullopt; is default value, not needed here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep this as it is now. Because all the other places in this file use this style. If we decide to remove, we can have another refactor PR.

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a well-structured, heavily-tested addition of the ConfidentialMPTMirrorUpdate transactor. I traced the preflight/preclaim/doApply logic for both issuer-mode and holder-self-migration flows (including epoch staleness checks, auditor late-registration, and the canonical-zero-inbox requirement for holder migrations) against the extensive test suite and didn't find a correctness or security bug in the changed code. ZKProof verification is intentionally deferred to a follow-up PR and is safe since the gating amendment (featureConfidentialMPTKeyRotation) is not yet enabled, as called out in the description. The one item below is a design-intent question rather than a confirmed bug.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Two moderate coverage and delegation findings remain unresolved, along with API documentation and comment fixes.

Review details

Suppressed comments (4)

Previously missed (1) — in code that hasn't changed since the last review.

include/xrpl/protocol/detail/transactions.macro:1147

  • This adds a new public transaction type and fields, but the PR does not update API-CHANGELOG.md to document the API addition. Please add an entry describing ConfidentialMPTMirrorUpdate, its fields, and the amendment gate so API consumers can discover the new submit/sign format.

src/test/app/ConfidentialMPTKeyRotation_test.cpp:1388

  • The comment has a subject–verb agreement error: “issuance” is singular, so “have” should be “has.”
        // The issuance have not enabled confidential balances.

src/test/jtx/impl/utility.cpp:81

  • The new type is added to the confidential fee auto-fill list, but the existing testConfidentialMPTBaseFee coverage still exercises only Convert, MergeInbox, Send, ConvertBack, and Clawback. A regression in this transaction's calculateBaseFee or fee handling would therefore pass; add MirrorUpdate to the normal, insufficient-fee, and excessive-fee checks.
        txType == jss::ConfidentialMPTClawback || txType == jss::ConfidentialMPTMirrorUpdate)

src/test/jtx/mpt.h:375

  • This transaction is declared delegable in include/xrpl/protocol/detail/transactions.macro:1141, but the new JTX argument has no delegate (or ticketSeq) member. Unlike the existing confidential argument structs in src/test/jtx/mpt.h:311-318 and :337-362, MPTTester::submit therefore cannot forward delegated signing for this transaction, so the issuer and holder migration flows have no delegated success/permission coverage despite the delegable-count change. Add those fields and delegated tests for both modes before expanding this surface.
    std::optional<Buffer> zkProof = std::nullopt;
  • Files reviewed: 14/14 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-structured, heavily-tested addition of the ConfidentialMPTMirrorUpdate transactor (issuer + holder self-migration modes for re-encrypting confidential MPT mirrors). The refactor of setMirrorEpochs into per-mirror helpers is clean and correctly wired into the new transactor's doApply. One correctness question worth resolving before merge: the preclaim precondition that gates an auditor-only migration on the issuer mirror already being current is applied only in issuer mode, not in holder self-migration mode, which looks like an unintentional asymmetry.

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ConfidentialMPTMirrorUpdate transaction is well-structured with thorough preflight/preclaim gating (issuer/holder mode discrimination, mirror-staleness checks, inbox-canonical-zero requirement for holder self-migration) and solid test coverage across both modes and multi-rotation scenarios. I did not find correctness or security bugs in the new transactor logic, epoch-setter split, or invariant wiring — the only note is a minor consistency nit on the new proof-length constant.

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a large, well-structured addition of the ConfidentialMPTMirrorUpdate transactor with thorough preflight/preclaim validation and an extensive test suite. I traced the field mappings in doApply (IssuerEncryptedAmount→IssuerEncryptedBalance, AuditorEncryptedAmount→AuditorEncryptedBalance) for the classic copy-paste swap bug and found them correct, and the epoch-setting refactor in ConfidentialTransfer.cpp is a clean, correct split of the prior combined function. I did not find high-confidence correctness or security bugs in the added lines; the one item below is a design-consistency question rather than a clear defect, and the ZKProof field being unverified is already explicitly and appropriately called out in the PR description as deferred, amendment-gated follow-up work.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Add the API changelog entry and delegate/ticket fields to the JTX helper.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

src/test/jtx/mpt.h:376

  • ConfidentialMPTMirrorUpdate is declared Delegation::Delegable in include/xrpl/protocol/detail/transactions.macro:1141, but this JTX argument type has no delegate or ticketSeq members. As a result, MPTTester::mirrorUpdate cannot construct delegated or ticket-based mirror updates, unlike the other delegable confidential-MPT helpers in this file, so those protocol paths cannot be exercised through the standard test helper. Add both optional fields so submit() can forward them.
    std::optional<Buffer> issuerEncryptedAmount = std::nullopt;
    std::optional<Buffer> auditorEncryptedAmount = std::nullopt;
    std::optional<Buffer> zkProof = std::nullopt;
    std::optional<XRPAmount> fee = std::nullopt;
  • Files reviewed: 15/15 changed files
  • Comments generated: 1
  • Review effort level: Lite

#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_MIRROR_UPDATE, 92, ConfidentialMPTMirrorUpdate,

@PeterChen13579 PeterChen13579 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

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.

4 participants