Conversation
Routes the dispute outcome onto the taker's bond when the operator has
opted in. Behaviour gate: enabled && apply_to ∈ {take, both} &&
slash_on_lost_dispute.
admin_settle_action and admin_cancel_action now call a new
bond::apply_taker_dispute_outcome_or_warn instead of the Phase 1
release helper. That entrypoint:
- reads the order kind (Sell-order taker = buyer; Buy-order taker =
seller) and combines it with the dispute outcome (admin_settle =
seller wins, admin_cancel = buyer wins) to decide whether the
taker lost;
- with the slash gate off OR the taker won, falls through to
release_bonds_for_order_or_warn — Phase 1 behaviour preserved by
default;
- with the gate on AND the taker lost, transitions the active taker
parent bond to pending-payout / lost-dispute via a conditional
UPDATE keyed on the active states only (Requested / Locked).
Phase 3's payout job then settles the bond hold invoice and pays
the winner — Phase 2 deliberately does no LND interaction here.
The lookup uses find_active_bonds_for_order filtered to BondRole::Taker
with parent_bond_id IS NULL so a Released prior-taker row from
supersede_prior_taker_bonds and (eventually) Phase 6 child rows are
both ignored.
Also a small Phase 2 hardening in on_bond_invoice_canceled: a bond
already in pending-payout is now left untouched on an LND-cancel
event. Without this, an LND CLTV-expiry cancel (which Phase 3 must
beat with settle_hold_invoice) would silently flip the row to
released and drop the slashed claim. Phase 1 behaviour for active
states (Requested / Locked → Released) is unchanged.
Tests cover:
- taker_lost_dispute truth table for the 4 (kind, seller_won) cells;
- the Phase 2 inertness guarantee (slash gate off without config);
- slash_taker_bond_for_lost_dispute happy path on Locked, defensive
Requested, idempotency on PendingPayout / Released, no-op when no
bond row exists, picking the active row over a Released prior-
taker row, ignoring Maker rows and Phase 6 child rows;
- apply_taker_dispute_outcome_or_warn falls through to release with
the gate off (Phase 1 contract);
- on_bond_invoice_canceled leaves PendingPayout alone but still
releases active bonds (Phase 1 regression guard).
Spec: docs/ANTI_ABUSE_BOND.md §7.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
WalkthroughThe PR implements taker-dispute outcome handling by replacing unconditional taker bond releases with outcome-driven processing. Admin cancel and settle actions now route taker bonds either to Phase 3 payout (when slashing is enabled) or release them, based on dispute results and configuration. Changes
Sequence DiagramsequenceDiagram
participant Admin as Admin Action
participant Outcome as Dispute Outcome Logic
participant BondFlow as Bond Flow Handler
participant DB as Database
Admin->>Outcome: apply_taker_dispute_outcome_or_warn(order, seller_won)
Outcome->>Outcome: Determine taker identity & dispute result
alt slash_on_lost_dispute enabled & taker lost
Outcome->>BondFlow: slash_taker_bond_for_lost_dispute()
BondFlow->>DB: UPDATE bond SET state=PendingPayout<br/>WHERE state IN (Requested, Locked)<br/>AND order_id=?
DB-->>BondFlow: Rows updated
BondFlow-->>Outcome: ✓ Slashed
else
Outcome->>BondFlow: release_bond() fallback
BondFlow->>DB: UPDATE bond SET state=Released<br/>WHERE id=?
DB-->>BondFlow: Row updated
BondFlow-->>Outcome: ✓ Released
end
Outcome-->>Admin: Processing complete
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Summary
Phase 2 of the anti-abuse bond rollout (issue #711, spec
docs/ANTI_ABUSE_BOND.md §7).
Routes a dispute outcome onto the taker's bond when the operator has
opted in.
Behaviour gate:
enabled && apply_to ∈ {take, both} && slash_on_lost_dispute.admin_settle_action(seller wins escrow) andadmin_cancel_action(buyer is refunded) now call a new
bond::apply_taker_dispute_outcome_or_warnin place of the Phase 1release helper. That entrypoint:
buyer; Buy-order taker = seller) and combines it with the dispute
outcome to decide whether the taker lost;
release_bonds_for_order_or_warn— Phase 1 behaviour preserved bydefault;
bond to
pending-payout/lost-disputevia a conditional UPDATEkeyed on the active states only (
Requested/Locked). Phase 3will settle the bond hold invoice and pay the winner — Phase 2
deliberately performs no LND interaction here.
find_active_bonds_for_orderfiltered toBondRole::Takerwithparent_bond_id IS NULL, so a releasedprior-taker row from
supersede_prior_taker_bondsand Phase 6 childrows are both ignored.
Phase 2 hardening of
on_bond_invoice_canceledA bond already in
pending-payoutis now left untouched on anLND-cancel event. Without this, an LND CLTV-expiry cancel (which
Phase 3 must beat with
settle_hold_invoice) would silently flip therow to
releasedand drop the slashed claim. Phase 1 behaviour foractive states (
Requested/Locked→Released) is unchanged.Tests
cargo test --bin mostrod: 266 passed. New tests inapp::bond::flow:taker_lost_dispute_truth_table— 4-cell(kind, seller_won)matrix.slash_on_lost_dispute_enabled_is_false_without_config— Phase 2inertness guarantee (no
[anti_abuse_bond]block ⇒ gate off).slash_taker_bond_transitions_locked_to_pending_payout— happy path.slash_taker_bond_transitions_requested_to_pending_payout— defensive.slash_taker_bond_skips_already_pending_payout— idempotent.slash_taker_bond_skips_terminal— no-op onReleased.slash_taker_bond_no_bond_is_noop— feature-off-when-trade-started.slash_taker_bond_picks_active_parent_among_prior_released— picksthe active row, not the released prior-taker row.
slash_taker_bond_ignores_maker_bond— taker-only scope.slash_taker_bond_ignores_phase6_child_rows— parent-only scope.apply_dispute_outcome_releases_with_flag_off— Phase 1 contractpreserved when the gate is off.
on_bond_invoice_canceled_leaves_pending_payout_untouched— Phase 2defense.
on_bond_invoice_canceled_still_releases_active_bond— Phase 1regression guard.
Test plan
cargo test --bin mostrod(266 passed)cargo clippy --all-targets --all-features -- -D warnings(clean)cargo fmt --check(clean)open dispute,
admin_settle→ bond row should bepending-payout/lost-dispute, hold invoice still lockedat LND.
open dispute,
admin_cancel→ bond row should bepending-payout/lost-dispute, hold invoice still lockedat LND.
(Phase 1 behaviour).
outcome (Phase 1 behaviour preserved).
Acceptance (from spec §7.3)
PendingPayout.Compatibility
columns (
state,slashed_reason).enabled = false, so existing nodes see nobehaviour change.
BondState::PendingPayoutrows are persisted but are not yetacted upon by any other subsystem; the Phase 3 PR will pick them
up.
🤖 Generated with Claude Code
Summary by CodeRabbit