Skip to content

fix(mpt): XLS-33 post-merge review follow-ups#336

Draft
e-desouza wants to merge 7 commits into
mainfrom
fix/mpt-review-followups
Draft

fix(mpt): XLS-33 post-merge review follow-ups#336
e-desouza wants to merge 7 commits into
mainfrom
fix/mpt-review-followups

Conversation

@e-desouza

Copy link
Copy Markdown
Collaborator

Why

Post-merge review follow-ups for the XLS-33 MPT support landed in #329. Each item came out of a four-stream + domain-persona review of the merged commit. No correctness or security defects were found in #329; these are hardening, de-duplication, and clarity changes.

What changed (one change per commit)

  1. fix(binarycodec): reject MPT amounts with cleared sign bit on decode — per XLS-33 MPT amounts are strictly unsigned; the decode path previously synthesized a negative value string instead of rejecting malformed wire data. Adds regression tests for is_positive() (was untested) and MPT decode boundaries (0, i64::MAX, mid).
  2. refactor(mpt): extract shared MPT validation into a neutral mpt_common module — transfer-fee/metadata/issuance-id/holder/domain validators were owned by the set module and reached into by every other MPT type. Moved to a flat shared module.
  3. test(common): restore error surfacing in ledger_accept / open_websocketlet _ = …await and bare .unwrap() had replaced .expect() + error_for_status(), swallowing failures and weakening test diagnostics.
  4. refactor(binarycodec): name MPT/IOU/sign leading-byte flag bits0x80/0x40/0x20 literals replaced with named constants. No behavior change.
  5. docs(models): warn that serde(untagged) on Amount/Currency is decode-inert — guard against a future maintainer deleting the hand-written Deserialize impls that prevent enum fallthrough.
  6. test(common): collapse duplicated MPT issuance helpers into one
  7. docs(changelog): note balance-parser MPT classification and MPT decode fix

How to validate

cargo test --features std,json-rpc,helpers,cli,websocket --lib
cargo clippy --lib --features std
cargo fmt --all --check

Local: 944 unit tests pass, clippy clean, zero build warnings, integration tests compile.

Review notes

Two review suggestions were deliberately deferred:

  • Moving the cleared-sign-bit rejection from the Serialize path into from_parser (whole-type invariant vs egress-path). Both reviewers rated the current placement acceptable; Amount is an opaque byte container by design.
  • Unifying the codec TryFrom amount classifier with the model Deserialize classifier — the two have a subtle intentional divergence (codec tolerates extra ICA keys); unifying risks behavior change and warrants a deliberate design pass.

Per XLS-33, MPT amounts are strictly unsigned — rippled's STAmount always
sets the positive bit (0x40) for MPT. The decode (Serialize) path previously
synthesized a negative value string when the sign bit was clear, producing a
value that would then fail re-validation at the model layer.

Reject the cleared-sign-bit case as malformed wire data instead. Adds
regression tests for is_positive() (which was reading the correct byte[0] but
had no coverage) and MPT decode value boundaries (0, i64::MAX, mid).
…n module

MPTokenIssuanceCreate and MPTokenIssuanceSet duplicated the transfer-fee cap
(50000), metadata byte limit (1024), and their validation bodies. The shared
validators (issuance-id, holder, domain-id, transfer-fee, metadata) previously
lived in the set module and were reached into by create, authorize, destroy,
clawback, and the MPTAmount/MPTCurrency models — an inverted dependency where
one consumer owned rules shared by all.

Move them to a new src/models/transactions/mpt_common.rs and repoint every
importer there, so all MPT transaction/model types depend on a flat shared
rule module rather than on each other.
…cket

ledger_accept() had its error handling replaced with `let _ = ...await`,
silently dropping transport failures and non-success statuses; a broken
standalone node would then surface as a confusing stale-ledger error far from
the root cause. Restore the .expect() + error_for_status() panics with
context. Likewise restore the descriptive .expect() messages in open_websocket
that had been downgraded to bare .unwrap().
The amount leading-byte bits (0x80 IOU, 0x40 positive, 0x20 MPT) appeared as
bare literals across is_native, is_mpt, is_positive, the type-length dispatch
in from_parser, the sign-handling decode path, and _serialize_mpt_amount.
Introduce named constants LEADING_IOU_FLAG / LEADING_MPT_FLAG /
LEADING_POS_FLAG and use them throughout. No behavior change.
…inert

The #[serde(untagged)] attribute only drives the derived Serialize; the
hand-written Deserialize impl handles decoding and exists specifically to
prevent enum fallthrough between the overlapping MPT/ICA/XRP variants. Add a
comment so a future maintainer does not 'simplify' by deleting the manual impl
and silently reintroduce the fallthrough bug.
create_mptoken_issuance and create_transferable_mptoken_issuance were
near-identical (~30 duplicated lines) differing only in the creation flag.
Extract a shared create_mptoken_issuance_with_flag and have both delegate to
it with their respective flag.
…e fix

Document two items surfaced in review: the metadata balance parser no longer
mislabels non-IOU amounts as XRP (MPT now returns None), and MPT amount decode
rejects a cleared sign bit as malformed.
@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.81%. Comparing base (e4fab93) to head (ffdd729).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #336      +/-   ##
==========================================
+ Coverage   85.77%   85.81%   +0.03%     
==========================================
  Files         235      236       +1     
  Lines       26828    26837       +9     
==========================================
+ Hits        23012    23029      +17     
+ Misses       3816     3808       -8     
Flag Coverage Δ
integration 71.52% <ø> (ø)
unit 86.50% <100.00%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/core/binarycodec/types/amount.rs 88.40% <100.00%> (+1.93%) ⬆️
src/models/amount/mod.rs 89.18% <ø> (ø)
src/models/amount/mpt_amount.rs 100.00% <ø> (ø)
src/models/currency/mod.rs 76.84% <ø> (ø)
src/models/currency/mpt_currency.rs 100.00% <ø> (ø)
src/models/transactions/clawback.rs 99.07% <ø> (ø)
src/models/transactions/mod.rs 56.37% <ø> (ø)
src/models/transactions/mpt_common.rs 100.00% <100.00%> (ø)
src/models/transactions/mptoken_authorize.rs 95.69% <ø> (ø)
src/models/transactions/mptoken_issuance_create.rs 96.24% <100.00%> (-0.16%) ⬇️
... and 2 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@pdp2121

pdp2121 commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

/ai-review

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

Copy link
Copy Markdown

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.

2 participants