Skip to content

fix: stop inbound payloads from restating what a trade is - #665

Open
AndreaDiazCorreia wants to merge 3 commits into
fix/replay-and-censorship-protectionfrom
fix/order-payload-replacement-guard
Open

fix: stop inbound payloads from restating what a trade is#665
AndreaDiazCorreia wants to merge 3 commits into
fix/replay-and-censorship-protectionfrom
fix/order-payload-replacement-guard

Conversation

@AndreaDiazCorreia

Copy link
Copy Markdown
Member

Depends on #660

Cut from fix/replay-and-censorship-protection, which rewrote the two files this builds on. Merge #660 first, or review against that branch.

What

Three payloads were taken at their word about things they should not have been able to state.

  • Trade terms. An inbound Order payload replaced the tracked order outright, so any later message could restate every economic field — including the payment method the trade screen renders as the account to send fiat to. The fiat terms now freeze once the order leaves pending, since nothing in the protocol renegotiates them. The sats amount stays free: a market-price order has none until it is taken.

    A PaymentRequest's embedded order no longer becomes the tracked one either. Its amount is the figure for that payment — the hold invoice is the order plus the seller's fee, the payout the order less the buyer's — so it was never the trade. The pay screen now reads its figure from the payment request, where the node states it, rather than from an order the payment request had overwritten.

  • Bond amounts. The node publishes in its kind-38385 event that it charges bonds, the percentage it takes and the floor it never goes below, and sizes every bond from exactly those. Nothing gated an inbound pay-bond-invoice on any of it. A request is refused when no policy was advertised, when it falls below the floor, and when it is not what the percentage yields for this order.

  • Trade key index. Index 0 is what the master identity is derived from, so deriving "trade key 0" hands back that key and a session built on it signs and runs ECDH under the identity a trade depends on not sharing. setCurrentKeyIndex has always refused it; derivation is reachable without going through the counter, and restore derives straight from an index in the response.

Notes

  • Where the node's arithmetic is mirrored, it is mirrored exactly — rounding included — because a client that computes differently refuses figures that are correct.
  • The bond amount check needs the order's sats amount. A market-priced range order is sized from the taker's own quote, which the node computes and never sends, so only the advertised floor is checked there.
  • The bond is held against the amount the node declares, not against the decoded invoice. Binding the two needs the BOLT-11 parser from the invoice PR; it is a small follow-up once that lands.
  • Conflicts with the invoice PR on pay_lightning_invoice_screen.dart: both change where the payment figure comes from. Keep this branch's reading from paymentRequest.order.
  • 11 existing test fixtures used trade index 0 as filler; none asserted it was valid.
  • flutter analyze clean; flutter test 1215 passing.

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • develop

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6c4e70c3-633c-4e77-bd3c-79ae82d3d43f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8b368fe0b0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// Bond acks (3.5) and the slash notice (4) carry a bond-sized SmallOrder
// with a null status; it was never the trade order.
if (isBondPayoutAck) return order;
if (message.payload is! Order) return order;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep the order baseline for payment requests

When a user takes an existing order, AbstractMostroNotifier starts with order: null, the outbound take carries an Amount or PaymentRequest rather than an Order, and the first node response can itself be a pay-invoice or pay-bond-invoice PaymentRequest. This return therefore leaves the tracked order null: the invoice screen renders fiat as 0 with no currency, while bond validation receives a null orderAmountSats and deliberately performs only the floor check, allowing an arbitrarily oversized bond. Seed the state from the public order/local take before discarding the embedded payment order.

Useful? React with 👍 / 👎.

Comment on lines +140 to +141
final info = ref.watch(orderRepositoryProvider).mostroInstance;
final instance = info == null ? null : MostroInstance.fromEvent(info);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Subscribe to asynchronous instance updates

If the bond screen opens before the kind-38385 info event arrives, mostroInstance is null and the legitimate bond is shown as unadvertised. Watching orderRepositoryProvider only watches the repository object's identity; mutation of its mostroInstance field does not rebuild this widget, even though the repository explicitly publishes later values through mostroInstanceStream. The refusal can therefore remain until an unrelated rebuild or route re-entry, blocking a valid bond payment during the normal startup race.

Useful? React with 👍 / 👎.

Comment on lines +145 to +147
advertised: instance?.bondPolicy == BondPolicy.enabled,
amountPct: instance?.bondAmountPct,
baseAmountSats: instance?.bondBaseAmountSats,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Enforce the policy's applicable side

When an enabled policy advertises bond_apply_to=take, this check still accepts a correctly sized maker bond, and likewise accepts a taker bond under a maker-only policy; a missing or malformed bondApplyTo is also ignored. Since MostroInstance explicitly models which side must lock the bond, requests that contradict that field can still make users lock funds and potentially be slashed despite the advertised policy. Include the current maker/taker context and require bondApplyTo to authorize that side.

Useful? React with 👍 / 👎.

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