Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 92 additions & 12 deletions src/cli/take_dispute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ pub async fn execute_admin_cancel_dispute(
println!("{table}");
println!("💡 Canceling dispute...\n");

let _admin_keys = get_admin_keys(ctx)?;
let admin_keys = get_admin_keys(ctx)?;

let payload = if slash_seller || slash_buyer {
Some(Payload::BondResolution(BondResolution {
Expand All @@ -84,17 +84,56 @@ pub async fn execute_admin_cancel_dispute(
None
};

// Build admin dispute message
let take_dispute_message =
let admin_cancel_message =
Message::new_dispute(Some(*dispute_id), None, None, Action::AdminCancel, payload)
.as_json()
.map_err(|_| anyhow::anyhow!("Failed to serialize message"))?;

admin_send_dm(ctx, take_dispute_message).await?;
// Send the message and await Mostro's reply so the success message is
// only printed when the cancel actually went through. Admin identity
// binds via the seal/rumor signers — the admin role doesn't rotate
// trade keys, so `admin_keys` signs both layers.
let sent_message = send_dm(
&ctx.client,
admin_keys,
admin_keys,
&ctx.mostro_pubkey,
admin_cancel_message,
None,
false,
);

println!("✅ Dispute canceled successfully!");
let recv_event = wait_for_dm(ctx, Some(admin_keys), sent_message)
.await
.map_err(|e| {
anyhow::anyhow!(
"Failed to receive response from Mostro for AdminCancel: {e}. \
The operation may not have completed; verify the order id exists and check backend logs."
)
})?;
Comment on lines +106 to +113
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

Don't collapse send failures into a "receive response" error.

wait_for_dm(...) also returns errors from sent_message.await?, so a publish/network/auth failure will be reported here as if Mostro just never replied. The added hint also says "order id", but these commands take a dispute_id, which makes the failure path harder to diagnose.

Possible adjustment
-    let recv_event = wait_for_dm(ctx, Some(admin_keys), sent_message)
-        .await
-        .map_err(|e| {
-            anyhow::anyhow!(
-                "Failed to receive response from Mostro for AdminCancel: {e}. \
-                 The operation may not have completed; verify the order id exists and check backend logs."
-            )
-        })?;
+    let recv_event = wait_for_dm(ctx, Some(admin_keys), sent_message)
+        .await
+        .map_err(|e| {
+            anyhow::anyhow!(
+                "AdminCancel failed while sending the request or waiting for Mostro's response: {e}. \
+                 Verify the dispute id and check client/backend logs."
+            )
+        })?;

Apply the same change to the settle path.

Also applies to: 198-205

🤖 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 `@src/cli/take_dispute.rs` around lines 106 - 113, The current map_err
collapses send (sent_message.await?) failures into a "receive response" error
from wait_for_dm; separately await and handle sent_message.await and map its
error to a publish/network/auth-specific message that mentions the dispute_id
and the operation (e.g., AdminCancel), then call wait_for_dm(...).map_err for
genuine receive-timeouts with the existing "no reply" hint (also referencing
dispute_id, not order id); apply the same change to the settle path (the
analogous sent_message.await and wait_for_dm sequence around lines 198-205).


Ok(())
let messages = parse_dm_events(recv_event, admin_keys, None).await;
let (message, _, sender_pubkey) = messages
.first()
.ok_or_else(|| anyhow::anyhow!("No response received from Mostro"))?;

if *sender_pubkey != ctx.mostro_pubkey {
return Err(anyhow::anyhow!("Received response from wrong sender"));
}

let message_kind = message.get_inner_message_kind();
if message_kind.action == Action::AdminCanceled {
println!("✅ Dispute canceled successfully!");
Ok(())
} else if message_kind.action == Action::CantDo {
print_commands_results(message_kind, ctx).await
} else {
Err(anyhow::anyhow!(
"Received response with mismatched action. Expected: {:?}, Got: {:?}",
Action::AdminCanceled,
message_kind.action
))
}
}

pub async fn execute_admin_settle_dispute(
Expand Down Expand Up @@ -126,7 +165,7 @@ pub async fn execute_admin_settle_dispute(
println!("{table}");
println!("💡 Settling dispute...\n");

let _admin_keys = get_admin_keys(ctx)?;
let admin_keys = get_admin_keys(ctx)?;

let payload = if slash_seller || slash_buyer {
Some(Payload::BondResolution(BondResolution {
Expand All @@ -137,15 +176,56 @@ pub async fn execute_admin_settle_dispute(
None
};

// Build admin dispute message
let take_dispute_message =
let admin_settle_message =
Message::new_dispute(Some(*dispute_id), None, None, Action::AdminSettle, payload)
.as_json()
.map_err(|_| anyhow::anyhow!("Failed to serialize message"))?;
admin_send_dm(ctx, take_dispute_message).await?;

println!("✅ Dispute settled successfully!");
Ok(())
// Send the message and await Mostro's reply so the success message is
// only printed when the settle actually went through. Admin identity
// binds via the seal/rumor signers — the admin role doesn't rotate
// trade keys, so `admin_keys` signs both layers.
let sent_message = send_dm(
&ctx.client,
admin_keys,
admin_keys,
&ctx.mostro_pubkey,
admin_settle_message,
None,
false,
);

let recv_event = wait_for_dm(ctx, Some(admin_keys), sent_message)
.await
.map_err(|e| {
anyhow::anyhow!(
"Failed to receive response from Mostro for AdminSettle: {e}. \
The operation may not have completed; verify the order id exists and check backend logs."
)
})?;

let messages = parse_dm_events(recv_event, admin_keys, None).await;
let (message, _, sender_pubkey) = messages
.first()
.ok_or_else(|| anyhow::anyhow!("No response received from Mostro"))?;

if *sender_pubkey != ctx.mostro_pubkey {
return Err(anyhow::anyhow!("Received response from wrong sender"));
}

let message_kind = message.get_inner_message_kind();
if message_kind.action == Action::AdminSettled {
println!("✅ Dispute settled successfully!");
Ok(())
} else if message_kind.action == Action::CantDo {
print_commands_results(message_kind, ctx).await
} else {
Err(anyhow::anyhow!(
"Received response with mismatched action. Expected: {:?}, Got: {:?}",
Action::AdminSettled,
message_kind.action
))
}
}

pub async fn execute_take_dispute(dispute_id: &Uuid, ctx: &Context) -> Result<()> {
Expand Down
3 changes: 3 additions & 0 deletions src/parser/dms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,9 @@ pub async fn print_commands_results(message: &MessageKind, ctx: &Context) -> Res
println!("📊 Please use a valid currency");
Err(anyhow::anyhow!("Invalid currency"))
}
Some(Payload::CantDo(Some(CantDoReason::NotFound))) => Err(anyhow::anyhow!(
"Resource not found. Verify the order or dispute id exists."
)),
_ => {
println!("❓ Unknown Error");
println!("💡 An unknown error occurred");
Expand Down
Loading