Skip to content

Support Confidential Transfers for MPTs (XLS-0096)#3364

Open
kuan121 wants to merge 12 commits into
mainfrom
confidential-mpts
Open

Support Confidential Transfers for MPTs (XLS-0096)#3364
kuan121 wants to merge 12 commits into
mainfrom
confidential-mpts

Conversation

@kuan121
Copy link
Copy Markdown
Collaborator

@kuan121 kuan121 commented Jun 5, 2026

High Level Overview of Change

This PR adds support for Confidential Multi-Purpose Tokens (XLS-0096).

Confidential MPT represents an MPT balance on-ledger as EC-ElGamal ciphertexts and uses zero-knowledge proofs, allowing validators to verify transfers (no overdraft, conservation of value) without learning the amounts. This change adds the wire-format definitions, the transaction and ledger models, field validation, fee handling, and a high-level builder API that encapsulates the cryptography. All changes are additive and do not alter existing behavior.

Components:

  • @xrplf/mpt-crypto (new package). A hex-in/hex-out TypeScript API over a vendored WebAssembly build of the reference C crypto library, providing ElGamal encryption, Pedersen commitments, blinding factors, context hashes, and the four proof builders (convert, convert-back, send, clawback). It is an optional peer dependency of xrpl, loaded lazily, and is intended to be published to the npm registry.
  • ripple-binary-codec. Definitions for the five new transaction types (ConfidentialMPTConvert, MergeInbox, ConvertBack, Send, Clawback), their fields, and the tecBAD_PROOF and temBAD_CIPHERTEXT result codes.
  • Transaction models (xrpl). The five ConfidentialMPT* interfaces and validate* functions, the tfMPTCanConfidentialAmount flag on MPTokenIssuanceCreate, and the IssuerEncryptionKey and AuditorEncryptionKey fields on MPTokenIssuanceSet.
  • Ledger models (xrpl). Confidential fields on MPToken (inbox, spending, and version balances; issuer- and auditor-encrypted balances; holder encryption key) and on MPTokenIssuance (issuer and auditor keys, outstanding amount).
  • Builder API (xrpl/confidential). The prepareConfidential* builders, which read the required ledger state, generate the ciphertexts, commitments, and proofs, and return an unsigned transaction. getConfidentialBalance decrypts a holder's spendable balance.
  • Fees (autofill). Confidential transactions carry a surcharge; the autofilled fee is base × (10 + signerCount), matching rippled.

Context of Change

This implements the client side of XLS-0096, tracking rippled PR #5860 and the xrpl-py reference implementation (branch confidential-mpt).

Key design decisions:

  • Cryptography is isolated in an optional package. The proof system requires a large WebAssembly library. Keeping it in @xrplf/mpt-crypto as an optional peer dependency, loaded lazily by the xrpl/confidential subpath, ensures that xrpl users who do not use Confidential MPT incur no additional dependency or load cost.
  • Encryption keys reuse the existing key infrastructure. The ElGamal encryption key is a distinct secp256k1 keypair, derived with ripple-keypairs. No new key type is introduced, the key is recoverable from a seed, and the approach is consistent with the Java SDK (xrpl4j).
  • Builders return sequence-pinned transactions. Each proof's context hash is bound to the transaction's identity (account, issuance, sequence, destination, balance version). The builders therefore pin Sequence, which must not be changed before submission.
  • The vendored WASM is version-pinned. The committed build must match the mpt-crypto version pinned by rippled; a mismatch results in tecBAD_PROOF.

Type of Change

  • New feature (non-breaking change which adds functionality)
  • Tests (You added tests for code that already exists, or your new feature included in this PR)

Did you update HISTORY.md?

  • Yes

Added Confidential MPT entries to the Unreleased sections of packages/xrpl/HISTORY.md and packages/ripple-binary-codec/HISTORY.md.

Test Plan

Unit, codec, and fee tests — run in CI with no additional setup (npm test):

  • packages/xrpl/test/models/ConfidentialMPT*.test.ts and the MPTokenIssuanceCreate / MPTokenIssuanceSet additions: each validate* function accepts well-formed transactions and rejects malformed fields.
  • packages/ripple-binary-codec/test/confidential-mpt.test.ts: encode/decode round-trips for the new transaction types and fields.
  • packages/xrpl/test/client/autofill.test.ts: the confidential fee calculation.

Integration testspackages/xrpl/test/integration/transactions/confidentialMPT*.test.ts: one file per transaction type plus a four-party lifecycle file, sharing confidentialMPTUtils.ts. These require a rippled with the MPTokensV1, Clawback, and ConfidentialTransfer amendments enabled. They run as part of npm run test:integration against the rippleci/xrpld develop image once #5860 is merged, and were verified against a local standalone rippled built from #5860: all five transaction types and the full four-party lifecycle (convert → merge → send → convert-back → clawback, with auditor disclosure) pass.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 5, 2026

Review Change Stack

Walkthrough

This PR introduces complete Confidential Multi-Purpose Tokens (XLS-0096) support across the XRPL.js ecosystem by adding a new cryptographic library, extending the binary codec, defining five new transaction types, and providing high-level builders for confidential MPT operations.

Changes

MPT Crypto Package

Layer / File(s) Summary
WASM module, types, and constants
packages/mpt-crypto/tsconfig.json, packages/mpt-crypto/src/module.ts, packages/mpt-crypto/src/types.ts, packages/mpt-crypto/src/constants.ts, packages/mpt-crypto/src/index.ts
WasmModule interface, public types (Keypair, Participant, PedersenParams, SendProofParams), and cryptographic byte-size constants establish the package API contract.
Hex/bytes conversion and WASM memory marshalling
packages/mpt-crypto/src/hex.ts, packages/mpt-crypto/src/internal.ts, packages/mpt-crypto/src/marshal.ts, packages/mpt-crypto/src/runtime.ts
hexToBytes/bytesToHex helpers with validation, Marshaller class for WASM memory management, and withModule runtime wrapper ensure safe cryptographic field serialization and cleanup.
Cryptographic primitives
packages/mpt-crypto/src/primitives.ts
Four async functions wrap WASM: generateBlindingFactor, encryptAmount, decryptAmount, and getPedersenCommitment (ElGamal encryption, decryption, Pedersen commitments).
Context hash generation
packages/mpt-crypto/src/context.ts
Four async functions compute 32-byte context hashes for each confidential MPT transaction type (convert, convert-back, send, clawback) by marshalling inputs and calling WASM _mpt_get_*_context_hash exports.
ZK proof generation
packages/mpt-crypto/src/proofs.ts
Four async functions generate hex-encoded proofs for each transaction type (getConvertProof, getClawbackProof, getConvertBackProof, getConfidentialSendProof) via WASM orchestration.
Package configuration and exports
packages/mpt-crypto/package.json, packages/mpt-crypto/eslint.config.js, packages/mpt-crypto/tsconfig.build.json, packages/mpt-crypto/tsconfig.eslint.json, packages/mpt-crypto/README.md, packages/mpt-crypto/src/index.ts, packages/mpt-crypto/wasm/mpt_crypto.js, lerna.json
Package metadata, npm scripts, ESLint/TypeScript build configs, public API re-exports, WASM UMD wrapper, and monorepo workspace registration.

Binary Codec

Layer / File(s) Summary
Field and transaction definitions
packages/ripple-binary-codec/src/enums/definitions.json
Added confidential field definitions (encrypted amounts, keys, ZK proof, commitments) to FIELDS, five new TRANSACTION_TYPES (ConfidentialMPT*), and error codes tecBAD_PROOF and temBAD_CIPHERTEXT.
Codec tests
packages/ripple-binary-codec/test/confidential-mpt.test.ts, packages/ripple-binary-codec/HISTORY.md
Round-trip encode/decode tests for all five Confidential MPT transaction types and confidential ledger entries, plus metadata regression test for tecBAD_PROOF.

XRPL Transaction Models and Validators

Layer / File(s) Summary
Five confidential transaction models
packages/xrpl/src/models/transactions/ConfidentialMPTConvert.ts, packages/xrpl/src/models/transactions/ConfidentialMPTConvertBack.ts, packages/xrpl/src/models/transactions/ConfidentialMPTSend.ts, packages/xrpl/src/models/transactions/ConfidentialMPTClawback.ts, packages/xrpl/src/models/transactions/ConfidentialMPTMergeInbox.ts
Each file defines a transaction interface extending BaseTransaction and exports a validator function enforcing required/optional field presence and hex-length constraints.
Extend existing MPT models with confidential support
packages/xrpl/src/models/ledger/MPToken.ts, packages/xrpl/src/models/ledger/MPTokenIssuance.ts, packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts, packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts
Added optional confidential balance fields (ConfidentialBalanceInbox, ConfidentialBalanceSpending), encryption keys, and version/outstanding-amount tracking; added tfMPTCanConfidentialAmount flag and encryption key validation.
Validation helpers and constants
packages/xrpl/src/models/transactions/common.ts
Exported CONFIDENTIAL_EC_POINT_BYTES, CONFIDENTIAL_ELGAMAL_CIPHERTEXT_BYTES, CONFIDENTIAL_BLINDING_FACTOR_BYTES constants and isHexBlob/isHexWithByteLength type-guard functions.
Transaction model exports and wiring
packages/xrpl/src/models/transactions/index.ts, packages/xrpl/src/models/transactions/transaction.ts
Re-export five Confidential MPT transaction classes and extend SubmittableTransaction union; update validate() dispatch switch to recognize and validate new transaction types.
Model validation unit tests
packages/xrpl/test/models/ConfidentialMPT*.test.ts, packages/xrpl/test/models/MPToken*.test.ts
Test suites for each transaction model covering success and failure validation paths; additional tests for MPToken flag and encryption key field additions.

XRPL Builders and Ledger Integration

Layer / File(s) Summary
Crypto loader and type
packages/xrpl/src/confidential/loader.ts
Lazy-loads optional @xrplf/mpt-crypto peer dependency via dynamic import with caching and error handling that instructs users to install if missing.
Ledger query and decryption utilities
packages/xrpl/src/confidential/ledger.ts
Exports helpers for address-to-AccountID conversion, account sequence queries, ledger_entry lookups for MPToken/MPTokenIssuance, and decryption of confidential balances using private keys.
Builder parameter types
packages/xrpl/src/confidential/types.ts
Defines ConfidentialKeypair and parameter interfaces for each builder (ConfidentialConvertParams, ConfidentialConvertBackParams, ConfidentialSendParams, ConfidentialClawbackParams, ConfidentialMergeInboxParams).
Transaction builders
packages/xrpl/src/confidential/convert.ts, packages/xrpl/src/confidential/transfer.ts
prepareConfidentialConvert, prepareConfidentialConvertBack, prepareConfidentialMergeInbox, prepareConfidentialSend, and prepareConfidentialClawback orchestrate crypto operations, ledger lookups, and transaction assembly.
Confidential integration layer
packages/xrpl/src/confidential/index.ts, packages/xrpl/package.json
Re-exports crypto loader, ledger utilities, builders, and types; updates package exports map with ./confidential subpath and marks @xrplf/mpt-crypto as optional peer dependency.
Fee calculation
packages/xrpl/src/sugar/autofill.ts
Introduces CONFIDENTIAL_FEE_MULTIPLIER and updates calculateFeePerTransactionType to apply extra base fees for Confidential MPT transaction types.
Integration test infrastructure
packages/xrpl/test/integration/confidentialMPTUtils.ts
Provides utilities to set up confidential clients, generate ElGamal keypairs, create/configure issuances, fund holders, and read encrypted balances for test flows.
End-to-end integration tests
packages/xrpl/test/integration/transactions/confidentialMPT*.test.ts
Five single-transaction test suites (convert, convert-back, send, clawback, merge-inbox) and a comprehensive 4-party lifecycle test verifying full flow correctness and auditor selective disclosure.
Package metadata
packages/xrpl/HISTORY.md, packages/xrpl/tsconfig.build.json
Documents XLS-96 support in changelog; adds TypeScript project reference to mpt-crypto package for build coordination.

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • XRPLF/xrpl.js#2874: Extends the transaction model wiring (SubmittableTransaction union and validate() dispatch) for new transaction types in similar ways.

Suggested reviewers

  • ckeshava
  • achowdhry-ripple
  • pdp2121

🐰 Confidential coins now hop along the blockchain,
With blinding factors and proofs of cryptic chain,
Holders encrypt their amounts in secret veil,
While auditors watch—without reading the mail!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch confidential-mpts

@kuan121 kuan121 marked this pull request as ready for review June 5, 2026 13:51
/** Free every allocation made through this marshaller. */
public dispose(): void {
for (const ptr of this.ptrs) {
this.mod._free(ptr)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: LOW

The dispose() method frees WASM heap allocations containing private keys and blinding factors without first zeroing the memory. Private key bytes (written via allocBytes in every proof/decrypt call) remain readable in the freed WASM heap until overwritten by later allocations, extending the window for key material extraction by same-process code (e.g., a compromised dependency reading HEAPU8).
Helpful? Add 👍 / 👎

💡 Fix Suggestion

Suggestion: To zero sensitive key material before freeing, the Marshaller must track the size of each allocation alongside its pointer. Change the ptrs field from number[] to an array of {ptr: number, size: number} tuples (line 45). Update alloc() (line 55) and allocBytes() (line 63) to push {ptr, size} instead of just ptr. Then, in dispose(), zero each region before freeing:

public dispose(): void {
  for (const { ptr, size } of this.ptrs) {
    this.mod.HEAPU8.fill(0, ptr, ptr + size)
    this.mod._free(ptr)
  }
  this.ptrs.length = 0
}

Specifically:

  1. Line 45: private readonly ptrs: Array<{ptr: number, size: number}> = []
  2. Line 55: this.ptrs.push({ptr, size})
  3. Line 63: this.ptrs.push({ptr, size: data.length})
  4. Lines 132-137: Update dispose() as shown above to HEAPU8.fill(0, ...) before _free().

This ensures private keys and blinding factors written via allocBytes are overwritten with zeros on the WASM heap before the memory is released back to the allocator.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
packages/ripple-binary-codec/test/confidential-mpt.test.ts (1)

36-139: 💤 Low value

ESLint func-names violations: consider using named function expressions.

The project's ESLint configuration requires named functions in test callbacks. All nine it() and one describe() callback currently use anonymous functions, triggering the func-names rule.

🎨 Example fix for consistency
-  it('round-trips ConfidentialMPTConvert (all fields)', function () {
+  it('round-trips ConfidentialMPTConvert (all fields)', function roundTripConfidentialMPTConvert() {
     assertRoundTrip({
       TransactionType: 'ConfidentialMPTConvert',

Apply the same pattern to the remaining eight tests and the describe() block.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ripple-binary-codec/test/confidential-mpt.test.ts` around lines 36 -
139, The test callbacks currently use anonymous functions (describe(...) and
each it(...)) which violates ESLint func-names; replace the anonymous callbacks
with named function expressions (e.g., name the describe callback like function
confidentialMptSuite() and each it callback with descriptive names such as
function roundTripsConfidentialMPTConvert(), function
roundTripsConfidentialMPTConvertBack(), function
roundTripsConfidentialMPTSend(), function roundTripsConfidentialMPTClawback(),
function roundTripsConfidentialMPTMergeInbox(), function
roundTripsMPTokenLedgerEntryWithConfidentialFields(), function
roundTripsMPTokenIssuanceLedgerEntryWithConfidentialFields(), and function
decodesTecBadProofInTransactionMetadata()) so the symbols describe and the nine
it calls keep the same behavior but satisfy func-names.
packages/mpt-crypto/package.json (1)

26-31: 💤 Low value

Consider declaring devDependencies explicitly.

The scripts reference tsc, jest, and eslint, but the package manifest does not declare these as devDependencies. While they may be hoisted from the workspace root in a Lerna monorepo, explicitly declaring them improves clarity and ensures the package's tooling requirements are documented.

📦 Suggested devDependencies block
   "engines": {
     "node": ">= 18"
-  }
+  },
+  "devDependencies": {
+    "typescript": "*",
+    "jest": "*",
+    "eslint": "*"
+  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mpt-crypto/package.json` around lines 26 - 31, The package.json
scripts ("build", "test", "lint", "clean") call tsc, jest and eslint but those
tools are not declared in this package's devDependencies; add explicit
devDependencies entries for at least typescript, jest, and eslint (and any
companion types/test runtime packages you require such as `@types/jest` or
ts-jest) to package.json so the package documents and installs its tooling
requirements even if the workspace root hoists them.
packages/xrpl/test/models/ConfidentialMPTConvert.test.ts (1)

1-172: ⚡ Quick win

Consider adding test coverage for missing IssuerEncryptedAmount.

The test suite covers most required fields, but IssuerEncryptedAmount (a required field per line 45 in ConfidentialMPTConvert.ts) has no corresponding "missing field" test. While the existing coverage is reasonable, adding this test would ensure all required encryption fields are verified.

✅ Suggested test case
+  it(`throws w/ missing IssuerEncryptedAmount`, function () {
+    assertInvalid(
+      {
+        TransactionType: 'ConfidentialMPTConvert',
+        Account: ACCOUNT,
+        MPTokenIssuanceID: MPT_ISSUANCE_ID,
+        MPTAmount: '100',
+        HolderEncryptedAmount: CIPHERTEXT,
+        BlindingFactor: BLINDING,
+      },
+      'ConfidentialMPTConvert: missing field IssuerEncryptedAmount',
+    )
+  })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/xrpl/test/models/ConfidentialMPTConvert.test.ts` around lines 1 -
172, Add a new test case in the ConfidentialMPTConvert suite that asserts a
missing IssuerEncryptedAmount triggers the expected validation error; use the
existing assertInvalid helper with a tx object containing TransactionType:
'ConfidentialMPTConvert', Account: ACCOUNT, MPTokenIssuanceID: MPT_ISSUANCE_ID,
MPTAmount: '100', HolderEncryptedAmount: CIPHERTEXT, BlindingFactor: BLINDING
(omit IssuerEncryptedAmount) and expect the message 'ConfidentialMPTConvert:
missing field IssuerEncryptedAmount' to mirror the other "missing field" tests
for consistency with validateConfidentialMPTConvert.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/mpt-crypto/package.json`:
- Around line 6-10: Add provenance documentation for the packaged WASM by
creating or updating a short README or inline comment that explains how
packages/mpt-crypto/wasm/mpt_crypto.wasm was produced or obtained: state the
upstream source/repo and commit/tag, the exact build command/tooling (e.g.,
Emscripten build command and environment), or the download URL and version, and
include integrity details such as a SHA256 checksum and any verification steps;
ensure this note is referenced from packages/mpt-crypto/src/module.ts (where the
Emscripten glue expects mpt_crypto.wasm) and that
packages/mpt-crypto/package.json's "files" list remains accurate.

In `@packages/mpt-crypto/src/marshal.ts`:
- Around line 52-57: The alloc function does not check for _malloc failure and
may use ptr==0; update alloc (and allocBytes) to verify const ptr =
this.mod._malloc(size) is non-zero before using it, throw or return a clear
error if ptr === 0, and avoid calling this.mod.HEAPU8.fill or pushing ptr into
this.ptrs when allocation failed (referencing the alloc and allocBytes methods,
this.mod._malloc, this.mod.HEAPU8.fill, and this.ptrs to locate the changes).

In `@packages/mpt-crypto/src/module.ts`:
- Line 124: The call to instance._mpt_secp256k1_context() returns a numeric
status that must be validated; update the code where
instance._mpt_secp256k1_context() is invoked to capture its return value, check
for non-zero (failure) and throw or return an error (or call process exit) when
initialization fails, following the same pattern used in context.ts (check
return === 0 for success). Ensure the check references the exact function name
_mpt_secp256k1_context and provides a clear error message denoting secp256k1
context initialization failure.

In `@packages/ripple-binary-codec/src/enums/definitions.json`:
- Around line 3492-3500: The BlindingFactor field currently uses nth: 39 which
collides with ReferenceHolding's nth: 39 for the Hash256 type; update
BlindingFactor's nth to the next unused ordinal (e.g., 40) in definitions.json
so each Hash256 field has a unique nth, ensuring the FieldLookup key
((Hash256_typeOrdinal << 16) | nth) no longer collides between BlindingFactor
and ReferenceHolding.

---

Nitpick comments:
In `@packages/mpt-crypto/package.json`:
- Around line 26-31: The package.json scripts ("build", "test", "lint", "clean")
call tsc, jest and eslint but those tools are not declared in this package's
devDependencies; add explicit devDependencies entries for at least typescript,
jest, and eslint (and any companion types/test runtime packages you require such
as `@types/jest` or ts-jest) to package.json so the package documents and installs
its tooling requirements even if the workspace root hoists them.

In `@packages/ripple-binary-codec/test/confidential-mpt.test.ts`:
- Around line 36-139: The test callbacks currently use anonymous functions
(describe(...) and each it(...)) which violates ESLint func-names; replace the
anonymous callbacks with named function expressions (e.g., name the describe
callback like function confidentialMptSuite() and each it callback with
descriptive names such as function roundTripsConfidentialMPTConvert(), function
roundTripsConfidentialMPTConvertBack(), function
roundTripsConfidentialMPTSend(), function roundTripsConfidentialMPTClawback(),
function roundTripsConfidentialMPTMergeInbox(), function
roundTripsMPTokenLedgerEntryWithConfidentialFields(), function
roundTripsMPTokenIssuanceLedgerEntryWithConfidentialFields(), and function
decodesTecBadProofInTransactionMetadata()) so the symbols describe and the nine
it calls keep the same behavior but satisfy func-names.

In `@packages/xrpl/test/models/ConfidentialMPTConvert.test.ts`:
- Around line 1-172: Add a new test case in the ConfidentialMPTConvert suite
that asserts a missing IssuerEncryptedAmount triggers the expected validation
error; use the existing assertInvalid helper with a tx object containing
TransactionType: 'ConfidentialMPTConvert', Account: ACCOUNT, MPTokenIssuanceID:
MPT_ISSUANCE_ID, MPTAmount: '100', HolderEncryptedAmount: CIPHERTEXT,
BlindingFactor: BLINDING (omit IssuerEncryptedAmount) and expect the message
'ConfidentialMPTConvert: missing field IssuerEncryptedAmount' to mirror the
other "missing field" tests for consistency with validateConfidentialMPTConvert.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cf049123-1b64-4467-9fdf-db79775c67d5

📥 Commits

Reviewing files that changed from the base of the PR and between f9d0a60 and 48d2dcf.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • packages/mpt-crypto/wasm/mpt_crypto.wasm is excluded by !**/*.wasm
📒 Files selected for processing (59)
  • lerna.json
  • packages/mpt-crypto/README.md
  • packages/mpt-crypto/eslint.config.js
  • packages/mpt-crypto/package.json
  • packages/mpt-crypto/src/constants.ts
  • packages/mpt-crypto/src/context.ts
  • packages/mpt-crypto/src/hex.ts
  • packages/mpt-crypto/src/index.ts
  • packages/mpt-crypto/src/internal.ts
  • packages/mpt-crypto/src/marshal.ts
  • packages/mpt-crypto/src/module.ts
  • packages/mpt-crypto/src/primitives.ts
  • packages/mpt-crypto/src/proofs.ts
  • packages/mpt-crypto/src/runtime.ts
  • packages/mpt-crypto/src/types.ts
  • packages/mpt-crypto/tsconfig.build.json
  • packages/mpt-crypto/tsconfig.eslint.json
  • packages/mpt-crypto/tsconfig.json
  • packages/mpt-crypto/wasm/mpt_crypto.js
  • packages/ripple-binary-codec/HISTORY.md
  • packages/ripple-binary-codec/src/enums/definitions.json
  • packages/ripple-binary-codec/test/confidential-mpt.test.ts
  • packages/xrpl/HISTORY.md
  • packages/xrpl/package.json
  • packages/xrpl/src/confidential/convert.ts
  • packages/xrpl/src/confidential/index.ts
  • packages/xrpl/src/confidential/ledger.ts
  • packages/xrpl/src/confidential/loader.ts
  • packages/xrpl/src/confidential/transfer.ts
  • packages/xrpl/src/confidential/types.ts
  • packages/xrpl/src/models/ledger/MPToken.ts
  • packages/xrpl/src/models/ledger/MPTokenIssuance.ts
  • packages/xrpl/src/models/transactions/ConfidentialMPTClawback.ts
  • packages/xrpl/src/models/transactions/ConfidentialMPTConvert.ts
  • packages/xrpl/src/models/transactions/ConfidentialMPTConvertBack.ts
  • packages/xrpl/src/models/transactions/ConfidentialMPTMergeInbox.ts
  • packages/xrpl/src/models/transactions/ConfidentialMPTSend.ts
  • packages/xrpl/src/models/transactions/MPTokenIssuanceCreate.ts
  • packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts
  • packages/xrpl/src/models/transactions/common.ts
  • packages/xrpl/src/models/transactions/index.ts
  • packages/xrpl/src/models/transactions/transaction.ts
  • packages/xrpl/src/sugar/autofill.ts
  • packages/xrpl/test/client/autofill.test.ts
  • packages/xrpl/test/integration/confidentialMPTUtils.ts
  • packages/xrpl/test/integration/transactions/confidentialMPTClawback.test.ts
  • packages/xrpl/test/integration/transactions/confidentialMPTConvert.test.ts
  • packages/xrpl/test/integration/transactions/confidentialMPTConvertBack.test.ts
  • packages/xrpl/test/integration/transactions/confidentialMPTLifecycle.test.ts
  • packages/xrpl/test/integration/transactions/confidentialMPTMergeInbox.test.ts
  • packages/xrpl/test/integration/transactions/confidentialMPTSend.test.ts
  • packages/xrpl/test/models/ConfidentialMPTClawback.test.ts
  • packages/xrpl/test/models/ConfidentialMPTConvert.test.ts
  • packages/xrpl/test/models/ConfidentialMPTConvertBack.test.ts
  • packages/xrpl/test/models/ConfidentialMPTMergeInbox.test.ts
  • packages/xrpl/test/models/ConfidentialMPTSend.test.ts
  • packages/xrpl/test/models/MPTokenIssuanceCreate.test.ts
  • packages/xrpl/test/models/MPTokenIssuanceSet.test.ts
  • packages/xrpl/tsconfig.build.json

Comment on lines +6 to +10
"files": [
"dist/*",
"wasm/*",
"src/*"
],
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check for the mpt_crypto.wasm binary in the package

fd -t f 'mpt_crypto.wasm' packages/mpt-crypto/

Repository: XRPLF/xrpl.js

Length of output: 98


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== WASM files =="
ls -la packages/mpt-crypto/wasm || true
echo

echo "== References to mpt_crypto.wasm =="
rg -n "mpt_crypto\.wasm" -S packages/mpt-crypto || true
echo

echo "== WASM provenance/build docs (best-effort search) =="
rg -n "(build|builds|download|provenance|source|compile|generated|toolchain|wasm)" -S packages/mpt-crypto || true

Repository: XRPLF/xrpl.js

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== packages/mpt-crypto/src/module.ts around locateFile / wasm comment =="
sed -n '80,140p' packages/mpt-crypto/src/module.ts

echo
echo "== Search for any explicit build/download provenance keywords =="
rg -n "(vendored|download|build|compile|emcc|emscripten|source|provenance|generated)" packages/mpt-crypto || true

echo
echo "== Files array in packages/mpt-crypto/package.json =="
cat packages/mpt-crypto/package.json

echo
echo "== Any README/docs mentioning wasm =="
fd -t f -e md -e txt packages/mpt-crypto | xargs -r rg -n "wasm|mpt_crypto\.wasm" || true

Repository: XRPLF/xrpl.js

Length of output: 50769


Document the provenance of mpt_crypto.wasm (binary is present and packaged)

  • packages/mpt-crypto/wasm/mpt_crypto.wasm exists and is covered by packages/mpt-crypto/package.json’s "files": ["wasm/*", ...].
  • packages/mpt-crypto/src/module.ts notes the Emscripten glue locates mpt_crypto.wasm next to mpt_crypto.js, but it doesn’t explain how the WASM was built/downloaded.
  • Add a short README/comment describing the WASM’s provenance (build command/tooling and source it comes from, or download process/checksums).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mpt-crypto/package.json` around lines 6 - 10, Add provenance
documentation for the packaged WASM by creating or updating a short README or
inline comment that explains how packages/mpt-crypto/wasm/mpt_crypto.wasm was
produced or obtained: state the upstream source/repo and commit/tag, the exact
build command/tooling (e.g., Emscripten build command and environment), or the
download URL and version, and include integrity details such as a SHA256
checksum and any verification steps; ensure this note is referenced from
packages/mpt-crypto/src/module.ts (where the Emscripten glue expects
mpt_crypto.wasm) and that packages/mpt-crypto/package.json's "files" list
remains accurate.

Comment on lines +52 to +57
public alloc(size: number): number {
const ptr = this.mod._malloc(size)
this.mod.HEAPU8.fill(0, ptr, ptr + size)
this.ptrs.push(ptr)
return ptr
}
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add null checks for _malloc failures.

The _malloc call can return 0 (null pointer) when allocation fails, but this isn't checked. Subsequent operations would then write to address zero or invalid memory, potentially corrupting the WASM heap.

🛡️ Proposed fix to add allocation checks
   public alloc(size: number): number {
     const ptr = this.mod._malloc(size)
+    if (ptr === 0) {
+      throw new Error(`Failed to allocate ${size} bytes`)
+    }
     this.mod.HEAPU8.fill(0, ptr, ptr + size)
     this.ptrs.push(ptr)
     return ptr
   }

Apply the same pattern to allocBytes() at line 61.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mpt-crypto/src/marshal.ts` around lines 52 - 57, The alloc function
does not check for _malloc failure and may use ptr==0; update alloc (and
allocBytes) to verify const ptr = this.mod._malloc(size) is non-zero before
using it, throw or return a clear error if ptr === 0, and avoid calling
this.mod.HEAPU8.fill or pushing ptr into this.ptrs when allocation failed
(referencing the alloc and allocBytes methods, this.mod._malloc,
this.mod.HEAPU8.fill, and this.ptrs to locate the changes).

const factory: ModuleFactory = require('../wasm/mpt_crypto')
const instance = await factory()
// Force one-time initialization of the shared secp256k1 context.
instance._mpt_secp256k1_context()
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validate the secp256k1 context initialization return code.

The _mpt_secp256k1_context() function returns a number (per the WasmModule interface), but the return value is not checked. Following the pattern used elsewhere (e.g., context.ts lines 30-33), WASM functions return 0 on success and non-zero on failure. If initialization fails, subsequent cryptographic operations may produce incorrect results or crash.

🛡️ Proposed fix to check initialization status
     const factory: ModuleFactory = require('../wasm/mpt_crypto')
     const instance = await factory()
     // Force one-time initialization of the shared secp256k1 context.
-    instance._mpt_secp256k1_context()
+    const status = instance._mpt_secp256k1_context()
+    if (status !== 0) {
+      throw new Error('secp256k1 context initialization failed')
+    }
     return instance
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
instance._mpt_secp256k1_context()
const factory: ModuleFactory = require('../wasm/mpt_crypto')
const instance = await factory()
// Force one-time initialization of the shared secp256k1 context.
const status = instance._mpt_secp256k1_context()
if (status !== 0) {
throw new Error('secp256k1 context initialization failed')
}
return instance
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mpt-crypto/src/module.ts` at line 124, The call to
instance._mpt_secp256k1_context() returns a numeric status that must be
validated; update the code where instance._mpt_secp256k1_context() is invoked to
capture its return value, check for non-zero (failure) and throw or return an
error (or call process exit) when initialization fails, following the same
pattern used in context.ts (check return === 0 for success). Ensure the check
references the exact function name _mpt_secp256k1_context and provides a clear
error message denoting secp256k1 context initialization failure.

Comment on lines +3492 to +3500
"BlindingFactor",
{
"isSerialized": true,
"isSigningField": true,
"isVLEncoded": false,
"nth": 39,
"type": "Hash256"
}
],
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.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Duplicate field ordinal for Hash256 type.

BlindingFactor is assigned nth: 39 within the Hash256 type, but ReferenceHolding (lines 1532–1540) already uses nth: 39 for the same type. This creates a field-ordinal collision (Hash256_typeOrdinal << 16) | 39, causing the codec's FieldLookup to overwrite one definition with the other. Serialization and deserialization will fail or produce incorrect results for one of these fields.

🔧 Proposed fix: assign a unique nth index

Assign BlindingFactor the next available nth index for Hash256 (40):

     [
       "BlindingFactor",
       {
         "isSerialized": true,
         "isSigningField": true,
         "isVLEncoded": false,
-        "nth": 39,
+        "nth": 40,
         "type": "Hash256"
       }
     ],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"BlindingFactor",
{
"isSerialized": true,
"isSigningField": true,
"isVLEncoded": false,
"nth": 39,
"type": "Hash256"
}
],
"BlindingFactor",
{
"isSerialized": true,
"isSigningField": true,
"isVLEncoded": false,
"nth": 40,
"type": "Hash256"
}
],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ripple-binary-codec/src/enums/definitions.json` around lines 3492 -
3500, The BlindingFactor field currently uses nth: 39 which collides with
ReferenceHolding's nth: 39 for the Hash256 type; update BlindingFactor's nth to
the next unused ordinal (e.g., 40) in definitions.json so each Hash256 field has
a unique nth, ensuring the FieldLookup key ((Hash256_typeOrdinal << 16) | nth)
no longer collides between BlindingFactor and ReferenceHolding.

@kuan121 kuan121 changed the title Confidential mpts Support Confidential Transfers for Multi-Purpose Tokens (XLS-0096) Jun 5, 2026
@kuan121 kuan121 changed the title Support Confidential Transfers for Multi-Purpose Tokens (XLS-0096) Support Confidential Transfers for MPTs (XLS-0096) Jun 5, 2026
@kuan121
Copy link
Copy Markdown
Collaborator Author

kuan121 commented Jun 5, 2026

/ai-review

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

No issues.

Review by Claude Sonnet 4.6 · Prompt: V15

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