Skip to content

[ANCHOR-1319]: Parse a muxed transaction source in the ledger helper - #2095

Open
ceciliaromao wants to merge 4 commits into
developfrom
fix/anchor-1319
Open

ceciliaromao wants to merge 4 commits into
developfrom
fix/anchor-1319

Conversation

@ceciliaromao

Copy link
Copy Markdown
Collaborator

Description

  • LedgerClientHelper.parseOperationAndSourceAccountAndMemo now accepts a transaction whose source is a muxed account (M..., CAP-0027), both for a V1 envelope and for the inner transaction of a fee bump. The source is resolved by a new resolveTransactionSource helper that switches on the MuxedAccount discriminant and returns the base G... address, the same shape resolveSourceAccount already returns for an operation source.
  • Before, the parser read the source with getEd25519() only, which is null for a muxed source, so it threw a NullPointerException. That exception is not a LedgerDecodeException, so it escaped Horizon.getTransaction and StellarRpc.fromGetTransactionResponse.
  • A source whose key type is neither ed25519 nor muxed ed25519 makes the parser log and return null, like its other unparseable-envelope cases. The XDR union has no other arm, so this only completes the switch.

Context

  • HackerOne report 4018874
  • A transaction from a muxed source made the ledger parser throw, which put the payment observer in PUBLISHER_ERROR on the same operation and made notify_onchain_funds_received / notify_onchain_funds_sent fail for that transaction. This is the fix for a reported security finding.
  • Wrapping the exception in LedgerDecodeException was ruled out: that path skips the operation and advances the cursor, which would silently drop a legitimate payment. The parser has to support the muxed arm instead.
  • The source is reduced to the G... address because that is what the rest of the platform expects (from_account is a G... everywhere, and Horizon reports source_account the same way). Nothing reads the source's muxed id.
  • The muxed destination byte order in convert() is a different bug and is not part of this change.

Testing

  • ./gradlew test
  • ./gradlew :core:test --tests "org.stellar.anchor.ledger.LedgerClientHelperTest": 6 new tests. A muxed V1 source and a muxed fee-bump inner source parse to the base G..., for muxed ids 0, 1 and 2^64-1. A payment without an operation source is attributed to the base G..., and a payment with its own operation source keeps it. An unknown key type returns null without throwing. The existing ed25519 V0, V1 and fee-bump tests are the controls.
  • ./gradlew :core:test --tests "org.stellar.anchor.ledger.MuxedTransactionSourceTest": 5 new tests that run a real muxed-source payment through Horizon.getTransaction (V1 and fee bump), StellarRpc.fromGetTransactionResponse (V1 and fee bump) and waitForTransactionAvailable.
  • All 11 new tests fail with the NullPointerException on develop and pass with this change.

Documentation

N/A: no documentation change, this is a bug fix.

Known limitations

  • The source's muxed id is not carried into LedgerTransaction.sourceAccount; only the base G... is kept.
  • The observers and Platform API handlers are not tested directly: their tests mock getTransaction, so the fix is covered one layer below, in the real Horizon and RPC parse paths.

@ceciliaromao ceciliaromao self-assigned this Nov 4, 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