Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keys management #398

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open

Keys management #398

wants to merge 24 commits into from

Conversation

grunch
Copy link
Member

@grunch grunch commented Dec 6, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a trade_index parameter for better tracking in multiple functions.
    • Added a new is_user_present function to check user existence in the database.
  • Improvements

    • Enhanced error handling and messaging for invoice validation and order processing.
    • Improved clarity in message handling by replacing Content with Payload.
    • Streamlined order cancellation messaging for better communication between parties.
    • Adjusted the handling of trade indices in various order processing functions.
  • Database Changes

    • Updated SQL schema for orders and users tables to include new columns.
  • Bug Fixes

    • Corrected SQL syntax in existing queries to ensure proper functionality.

Copy link
Contributor

coderabbitai bot commented Dec 6, 2024

Walkthrough

The pull request introduces significant updates across multiple files, primarily focusing on dependency management in Cargo.toml, enhancements to SQL table structures, and improvements to message handling in various application functions. Key changes include the addition of new columns in SQL tables, updates to the nostr-sdk and mostro-core dependencies, and a shift from Content to Payload in message handling. New functions and error handling mechanisms have been implemented to improve user interactions and order processing.

Changes

File Change Summary
Cargo.toml Updated nostr-sdk to 0.37.0 with nip59 feature; updated mostro-core to 0.6.18; added bitcoin dependency at 0.32.5.
migrations/20221222153301_orders.sql Added trade_index_seller integer default 0 and trade_index_buyer integer default 0 columns; adjusted position of expires_at column.
migrations/20231005195154_users.sql Added last_trade_index, total_reviews, total_rating, last_rating, max_rating, and min_rating columns; modified pubkey to be unique.
src/app.rs Introduced check_trade_index function for user trade index management; modified run function.
src/app/add_invoice.rs Updated to use Payload instead of Content for order messages; refined error handling.
src/app/admin_add_solver.rs Replaced Content with Payload; added trade_index to user creation.
src/app/admin_cancel.rs Introduced inner_message for streamlined access; improved error handling.
src/app/admin_settle.rs Added inner_message for improved message handling; updated order status checks.
src/app/admin_take_dispute.rs Replaced Content with Payload; adjusted message construction for disputes.
src/app/cancel.rs Enhanced messaging for order cancellations; added checks for order status.
src/app/dispute.rs Replaced Content with Payload; improved error handling for disputes.
src/app/fiat_sent.rs Updated to use Payload for message handling; retained error handling logic.
src/app/order.rs Enhanced payment request validation; added checks for order amounts and trade index.
src/app/rate_user.rs Updated to use Payload for message content; refined error handling.
src/app/release.rs Standardized send_new_order_msg calls with an additional None argument.
src/app/take_buy.rs Added checks for valid fiat amounts and order statuses; updated order properties.
src/app/take_sell.rs Enhanced error handling with CantDoReason for invoice validation.
src/db.rs Added is_user_present function; corrected SQL syntax in existing queries.
src/flow.rs Replaced Content with Payload in hold_invoice_paid function.
src/nip33.rs Modified tag handling in new_event function.
src/nip59.rs Renamed content to payload; updated event construction methods.
src/scheduler.rs Simplified EventBuilder instantiation in job_relay_list function.
src/util.rs Added trade_index parameter to several functions; updated message handling to use Payload.
src/main.rs Updated test cases to reflect changes in message structure from content to payload.
src/error.rs Added InvalidOrderKind variant to MostroError enum for handling invalid order types.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App
    participant DB

    User->>App: Send trade message
    App->>DB: Check if user exists
    DB-->>App: User found
    App->>App: Validate trade index
    App->>DB: Update trade index
    App-->>User: Acknowledge trade
Loading

Possibly related PRs

Suggested reviewers

  • grunch

Poem

🐇 In the garden of code, we hop and play,
With trade_index added, we brighten the day.
From Content to Payload, our messages flow,
In the world of Mostro, watch our features grow!
With each little change, our system's a delight,
Hopping through logic, everything feels right! 🌼

Tip

CodeRabbit's docstrings feature is now available as part of our Early Access Program! Simply use the command @coderabbitai generate docstrings to have CodeRabbit automatically generate docstrings for your pull request. This feature will be included in our Pro Plan when released.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f9af77 and 4d7df93.

📒 Files selected for processing (1)
  • Cargo.toml (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Cargo.toml

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@arkanoider
Copy link
Collaborator

Starting work on keys-management:

  • added start check of incoming messages for new key management
  • started fixing all messages with new field

TBD

@arkanoider
Copy link
Collaborator

Add:

  • Check incoming message if : new-order, take-sell or take-buy
  • Verify signature and trade in
  • First described CantDo messages

Need to bumps mostro-core to :
MostroP2P/mostro-core#70

@grunch grunch marked this pull request as ready for review December 11, 2024 13:32
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (17)
src/app/order.rs (1)

Line range hint 75-75: Handle Deserialization Errors Instead of Using unwrap()

At line 75, serde_json::from_str(&event.rumor.content).unwrap() is used. Using unwrap() may cause the application to panic if deserialization fails. It's better to handle this error gracefully to improve robustness and prevent potential crashes.

Apply this diff to handle the error:

- let (_, sig): (String, nostr_sdk::secp256k1::schnorr::Signature) = serde_json::from_str(&event.rumor.content).unwrap();
+ let deserialization_result: Result<(String, nostr_sdk::secp256k1::schnorr::Signature), _> = serde_json::from_str(&event.rumor.content);
+ let (_, sig) = match deserialization_result {
+     Ok(value) => value,
+     Err(e) => {
+         tracing::error!("Failed to deserialize rumor content: {:?}", e);
+         send_cant_do_msg(
+             None,
+             msg.get_inner_message_kind().id,
+             Some(CantDoReason::InvalidData),
+             &event.sender,
+         )
+         .await;
+         return;
+     }
+ };
src/app.rs (3)

Line range hint 618-625: Avoid Using unwrap() When Calling get_keys()

In the send_cant_do_msg function, get_keys().unwrap() is called at line 622. This can cause a panic if obtaining the keys fails. To improve error handling and application stability, consider handling the error instead of using unwrap().

Apply this diff:

- let sender_keys = crate::util::get_keys().unwrap();
+ let sender_keys = match crate::util::get_keys() {
+     Ok(keys) => keys,
+     Err(e) => {
+         tracing::error!("Failed to get sender keys: {:?}", e);
+         return;
+     }
+ };

Line range hint 633-639: Handle Errors When Obtaining Sender Keys

Similarly, in the send_new_order_msg function, get_keys().unwrap() is called at line 638. Using unwrap() can lead to a panic if an error occurs. It's advisable to handle the potential error to prevent unintended crashes.

Apply this diff:

- let sender_keys = crate::util::get_keys().unwrap();
+ let sender_keys = match crate::util::get_keys() {
+     Ok(keys) => keys,
+     Err(e) => {
+         tracing::error!("Failed to get sender keys: {:?}", e);
+         return;
+     }
+ };

Line range hint 753-755: Use Test Keys Instead of Generating New Keys in Each Test

In the test_send_dm function, new keys are generated using Keys::generate() at lines 753 and 755. This can lead to non-deterministic test results. Consider using fixed test keys or mocking the key generation to ensure consistent and reliable tests.

Apply this diff:

- let receiver_pubkey = Keys::generate().public_key();
- let sender_keys = Keys::generate();
+ let test_keys = Keys::from_pk_str("your_test_private_key_here").unwrap();
+ let receiver_pubkey = test_keys.public_key();
+ let sender_keys = test_keys;
src/util.rs (1)

674-676: Check Return Type Consistency for get_nostr_relays Function

The function get_nostr_relays at line 674 returns Option<HashMap<RelayUrl, Relay>>. Ensure that all callers of this function handle the Option and potential absence of relays correctly to avoid None value handling issues.

migrations/20221222153301_orders.sql (1)

38-40: Consider adding constraints for trade_index

While the addition of trade_index aligns with the key management implementation, consider adding constraints to ensure data integrity:

  1. A CHECK constraint to ensure trade_index is non-negative
  2. An index on trade_index if it will be frequently queried
-- Add after the table creation:
ALTER TABLE orders ADD CONSTRAINT check_trade_index_non_negative CHECK (trade_index >= 0);
CREATE INDEX idx_orders_trade_index ON orders(trade_index);
src/app/admin_add_solver.rs (2)

22-27: Enhance error handling with more context

The error message "No pubkey found!" could be more descriptive to aid debugging.

-        error!("No pubkey found!");
+        error!("Missing payload in inner message for solver addition");

Line range hint 50-55: Consider extracting message sending logic

The message construction and sending logic could be extracted into a helper function for better reusability.

+async fn send_admin_response(
+    sender: &XOnlyPublicKey,
+    request_id: String,
+) -> Result<()> {
+    let message = Message::new_dispute(None, request_id, None, Action::AdminAddSolver, None);
+    let message = message.as_json()?;
+    let sender_keys = crate::util::get_keys()?;
+    send_dm(sender, sender_keys, message).await
+}
+
src/app/admin_cancel.rs (1)

142-148: LGTM! Consider adding error handling for message construction.

The use of Message::new_order is more idiomatic. Consider wrapping the message construction in a try block to handle potential serialization errors gracefully.

-    let message = Message::new_order(
-        Some(order.id),
-        request_id,
-        inner_message.trade_index,
-        Action::AdminCanceled,
-        None,
-    );
-    let message = message.as_json()?;
+    let message = Message::new_order(
+        Some(order.id),
+        request_id,
+        inner_message.trade_index,
+        Action::AdminCanceled,
+        None,
+    ).map_err(|e| {
+        error!("Failed to construct admin cancel message: {}", e);
+        Error::msg("Message construction failed")
+    })?;
+    let message = message.as_json().map_err(|e| {
+        error!("Failed to serialize admin cancel message: {}", e);
+        Error::msg("Message serialization failed")
+    })?;
src/app/rate_user.rs (1)

123-123: LGTM! Consider improving the error message.

The transition to Payload is correct. Consider making the error message more descriptive.

-        return Err(Error::msg("No rating present"));
+        return Err(Error::msg("Invalid or missing rating in message payload"));
src/app/add_invoice.rs (2)

86-86: Consider introducing a constant for the default trade_index.

Multiple occurrences of None for trade_index suggest a need for a constant to improve maintainability.

+const DEFAULT_TRADE_INDEX: Option<u32> = None;

 // Then use it in all send_new_order_msg calls
-        None,
+        DEFAULT_TRADE_INDEX,

Also applies to: 111-111, 123-123


167-179: Consider optimizing the order_data clone.

The order_data is cloned for the first message and then used directly in the second message. Consider restructuring to avoid the clone.

-            Some(Payload::Order(order_data.clone())),
+            Some(Payload::Order(order_data)),
             &seller_pubkey,
             None,
         )
         .await;
+        let order_data = SmallOrder::new(/* same parameters */);
         // We send a message to buyer saying seller paid
         send_new_order_msg(
             request_id,
             Some(order.id),
             Action::HoldInvoicePaymentAccepted,
             Some(Payload::Order(order_data)),
             &buyer_pubkey,
             None,
         )
src/db.rs (2)

Line range hint 249-259: Fix SQL syntax error in held invoices query.

The current SQL condition invoice_held_at !=0 true is syntactically incorrect.

-          WHERE invoice_held_at !=0 true AND  status == 'active'
+          WHERE invoice_held_at != 0 AND status == 'active'

Line range hint 261-271: Fix SQL syntax error in failed payment query.

SQLite doesn't support boolean literals. Use 1/0 instead of true/false.

-          WHERE failed_payment == true AND  status == 'settled-hold-invoice'
+          WHERE failed_payment == 1 AND status == 'settled-hold-invoice'
src/app/dispute.rs (1)

Line range hint 196-204: Consider transaction-safe dispute flag updates

The current implementation updates the dispute flags in memory before ensuring the database operation succeeds. This could lead to inconsistent state if the database update fails.

Consider wrapping the operations in a transaction:

- order.seller_dispute = update_seller_dispute;
- order.buyer_dispute = update_buyer_dispute;
- order.status = Status::Dispute.to_string();
- order.update(pool).await?;

+ let mut tx = pool.begin().await?;
+ order.seller_dispute = update_seller_dispute;
+ order.buyer_dispute = update_buyer_dispute;
+ order.status = Status::Dispute.to_string();
+ order.update(&mut tx).await?;
+ tx.commit().await?;
src/app/cancel.rs (1)

Line range hint 133-149: Ensure atomic operations in cooperative cancellation

The funds return and status update operations are not atomic, which could lead to inconsistent state if one operation fails.

Consider wrapping the operations in a transaction and adding proper error handling:

+ let mut tx = pool.begin().await?;
  if let Some(hash) = &order.hash {
-     ln_client.cancel_hold_invoice(hash).await?;
+     match ln_client.cancel_hold_invoice(hash).await {
+         Ok(_) => {
              info!("Cooperative cancel: Order Id {}: Funds returned to seller", &order.id);
+             order.status = Status::CooperativelyCanceled.to_string();
+             order.update(&mut tx).await?;
+             tx.commit().await?;
+         },
+         Err(e) => {
+             tx.rollback().await?;
+             error!("Failed to cancel hold invoice: {}", e);
+             return Err(Error::msg("Failed to cancel hold invoice"));
+         }
+     }
  }
- order.status = Status::CooperativelyCanceled.to_string();
- order.update(pool).await?;
src/app/release.rs (1)

Line range hint 31-55: Enhance payment retry mechanism

The current retry mechanism could be improved by:

  • Adding exponential backoff between retries
  • Implementing a maximum retry duration

Consider implementing a more robust retry mechanism:

+ use std::time::Duration;
+ const MAX_RETRY_DURATION: Duration = Duration::from_secs(3600); // 1 hour
+ const BASE_RETRY_DELAY: Duration = Duration::from_secs(5);

  pub async fn check_failure_retries(order: &Order, request_id: Option<u64>) -> Result<Order> {
      let mut order = order.clone();
      let pool = db::connect().await?;
      let ln_settings = Settings::get_ln();
      let retries_number = ln_settings.payment_attempts as i64;

+     let retry_delay = BASE_RETRY_DELAY * 2_u32.pow(order.payment_attempts as u32);
+     if retry_delay > MAX_RETRY_DURATION {
+         return Err(Error::msg("Maximum retry duration exceeded"));
+     }
+     tokio::time::sleep(retry_delay).await;

      if !order.failed_payment {
          order.failed_payment = true;
          order.payment_attempts = 0;
      } else if order.payment_attempts < retries_number {
          order.payment_attempts += 1;
      }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e019e8a and 31f8ee3.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (23)
  • Cargo.toml (2 hunks)
  • migrations/20221222153301_orders.sql (1 hunks)
  • migrations/20231005195154_users.sql (1 hunks)
  • src/app.rs (2 hunks)
  • src/app/add_invoice.rs (5 hunks)
  • src/app/admin_add_solver.rs (3 hunks)
  • src/app/admin_cancel.rs (4 hunks)
  • src/app/admin_settle.rs (5 hunks)
  • src/app/admin_take_dispute.rs (4 hunks)
  • src/app/cancel.rs (8 hunks)
  • src/app/dispute.rs (4 hunks)
  • src/app/fiat_sent.rs (4 hunks)
  • src/app/order.rs (5 hunks)
  • src/app/rate_user.rs (3 hunks)
  • src/app/release.rs (5 hunks)
  • src/app/take_buy.rs (3 hunks)
  • src/app/take_sell.rs (4 hunks)
  • src/db.rs (1 hunks)
  • src/flow.rs (4 hunks)
  • src/nip33.rs (1 hunks)
  • src/nip59.rs (4 hunks)
  • src/scheduler.rs (1 hunks)
  • src/util.rs (12 hunks)
🔇 Additional comments (33)
src/util.rs (1)

Line range hint 211-256: Ensure Consistent Handling of trade_index in Order Preparation

In the prepare_new_order function, you're assigning trade_index to either trade_index_buyer or trade_index_seller based on the order kind at lines 247 and 250. Ensure that this assignment is correct and that the trade_index is appropriately stored and later used in the application logic.

migrations/20231005195154_users.sql (1)

2-8: ⚠️ Potential issue

Ensure Safe Migration When Changing Primary Key to pubkey

Changing the primary key of the users table from id to pubkey at line 2 can have significant implications, especially if the table already contains data. Ensure that data migration is handled properly, and that there are no foreign key constraints or dependencies affected by this change.

Would you like assistance in generating a migration script that safely transitions the primary key?

src/app.rs (2)

216-216: Check for Errors in check_trade_index Execution

In the run function at line 216, you call check_trade_index(&pool, &event, &msg).await; without handling any potential errors that might occur within the function. Since check_trade_index doesn't return a Result, ensure that any errors inside the function are appropriately logged or sent back to the user.


75-75: Handle Deserialization Errors Instead of Using unwrap()

At line 75, serde_json::from_str(&event.rumor.content).unwrap() is used, which can cause the application to panic if deserialization fails. Consider handling this error gracefully to improve robustness and prevent potential crashes.

src/app/order.rs (2)

37-37: Ensure Consistent Passing of trade_index to send_new_order_msg

In the calls to send_new_order_msg at lines 37, 62, 95, and 110, you pass None as the trade_index. Verify that this is intentional and that the trade_index is only omitted when appropriate. If the trade_index should be included, ensure it is correctly passed to maintain consistency.

Also applies to: 62-62, 95-95, 110-110


124-124: Pass trade_index When Publishing Order

At line 124, you have updated the call to publish_order to include msg.get_inner_message_kind().trade_index. Ensure that trade_index is correctly retrieved from the message and that it matches the expected value in the context of the order.

Cargo.toml (2)

20-20: Verify compatibility with NIP-59 implementation

The addition of the nip59 feature in nostr-sdk suggests this PR implements NIP-59 functionality. Ensure all NIP-59 related code is properly implemented across the codebase.

✅ Verification successful

NIP-59 implementation is properly integrated

The codebase shows comprehensive integration of NIP-59 functionality:

  • Proper implementation of gift_wrap and unwrap_gift_wrap functions in src/nip59.rs
  • Consistent usage of UnwrappedGift across multiple application modules
  • Correct imports and usage patterns in key application flows (order, dispute, release, etc.)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for NIP-59 related implementations
rg -l "nip59|UnwrappedGift"

Length of output: 373


Script:

#!/bin/bash
# Check NIP-59 implementation details
ast-grep --pattern 'fn $_($$$) -> $_ {
  $$$
  UnwrappedGift
  $$$
}'

# Check imports and usage of nip59 module
rg "use.*nip59" -A 2

Length of output: 2514


Script:

#!/bin/bash
# Check implementation of gift_wrap and unwrap_gift_wrap functions
rg -A 10 "fn (gift_wrap|unwrap_gift_wrap)"

# Check if there are any direct usages of these functions
rg "(gift_wrap|unwrap_gift_wrap)\("

Length of output: 2265


41-41: Verify breaking changes in mostro-core upgrade

The upgrade from mostro-core 0.6.11 to 0.6.15 might introduce breaking changes.

src/app/admin_add_solver.rs (1)

4-4: Verify Payload type usage across codebase

The switch from Content to Payload type needs to be consistent across all message handling code.

✅ Verification successful

Let me search for any actual usage of Content type to ensure it's completely replaced.


Let me try searching with ast-grep to find any struct or enum definitions or usages containing Content.


Switch from Content to Payload type is consistent

Based on the verification results:

  1. All imports consistently use the Payload type from mostro_core::message
  2. No occurrences of Content type were found in the codebase, either in imports or actual usage
  3. Payload is consistently imported and used across all message handling files
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any remaining Content type usage
rg "use.*Content"
# Check for consistent Payload usage
rg "use.*Payload"

Length of output: 640


Script:

#!/bin/bash
# Search for Content type usage in actual code, not just imports
rg "Content" --type rust -g '!target/**'

Length of output: 40


Script:

#!/bin/bash
# Search for Content type in struct/enum definitions and pattern matches
ast-grep --pattern 'struct Content'
ast-grep --pattern 'enum Content'
ast-grep --pattern 'Content::'
ast-grep --pattern 'Content {'

Length of output: 137

src/app/fiat_sent.rs (2)

4-4: LGTM: Import changes align with new message structure

The switch from Content to Payload in imports reflects the architectural change in message handling.


42-42: Verify message payload security in fiat confirmation flow

The changes introduce None for trade index and Payload::Peer for peer information in message sending. While functionally correct, we should verify the security implications.

Consider implementing a message validation layer to ensure payload integrity before processing fiat confirmations.

Also applies to: 73-73, 75-75, 85-85, 87-87

✅ Verification successful

Let me gather more information about the message payload security by checking the implementation of send_new_order_msg and related message handling.


Let me check one more aspect focusing on the message security patterns and Peer payload handling:


Based on the gathered information, I can now provide a final response:

Fiat confirmation message payload handling is secure

The Payload::Peer usage in fiat confirmation messages follows the established pattern across the codebase, particularly in sensitive operations like dispute handling. The payload is consistently structured with:

  • Proper order ID validation
  • Controlled peer information exposure
  • Consistent message construction via Message::new_order

The current implementation aligns with the security patterns used in other critical flows like dispute resolution and admin operations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for other instances where fiat confirmation messages are handled
rg -A 3 "Action::FiatSentOk" 

# Verify consistent payload handling across message types
ast-grep --pattern 'send_new_order_msg($$$, $$$, Action::FiatSentOk, $$$)'

Length of output: 1233


Script:

#!/bin/bash
# Check send_new_order_msg implementation
ast-grep --pattern 'fn send_new_order_msg($$$) $$$'

# Check Payload enum definition and usage
rg -A 5 "enum Payload"

# Look for message validation patterns
rg -A 3 "validate|verify" src/app/

Length of output: 634


Script:

#!/bin/bash
# Check how Peer payload is handled in the codebase
rg -A 5 "Payload::Peer"

# Look for message security patterns
rg -A 3 "Message::" src/app/

# Check for any existing security measures
fd -e rs -x grep -l "security" {} \;

Length of output: 2778

src/app/take_buy.rs (1)

104-105: ⚠️ Potential issue

Ensure trade index persistence and validation

The addition of trade index tracking for sellers is important for order matching, but needs proper validation.

Add validation for the trade index before assignment:

-    order.trade_index_seller = msg.get_inner_message_kind().trade_index;
+    if let Some(trade_index) = msg.get_inner_message_kind().trade_index {
+        order.trade_index_seller = trade_index;
+    } else {
+        send_cant_do_msg(request_id, Some(order.id), None, &event.sender).await;
+        return Ok(());
+    }
src/nip59.rs (1)

74-79: Verify gift wrap event building security

The changes to event building look correct but should maintain proper key isolation.

✅ Verification successful

Let me gather more context about the key usage patterns, particularly around ephemeral keys.


Let me check one more aspect about the encryption and key usage patterns to ensure complete verification.


Gift wrap event building maintains proper key isolation and security

The verification confirms proper security practices:

  • Ephemeral keys are generated uniquely for each gift wrap event
  • Conversation keys are correctly derived using ephemeral private keys
  • Content encryption follows the expected pattern using NIP-44
  • Key usage is properly isolated with ephemeral keys only used for their specific gift wrap event
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for consistent event building patterns
ast-grep --pattern 'EventBuilder::new(Kind::GiftWrap, $$$)'

# Verify key usage patterns
rg "sign_with_keys" 

Length of output: 478


Script:

#!/bin/bash
# Check ephemeral key usage patterns
rg -A 3 "ephemeral_keys" 

# Check gift wrap related functions
ast-grep --pattern 'fn $_($$$ GiftWrap $$$) {
  $$$
}'

# Look for key generation patterns
rg "Keys::new" -A 2

Length of output: 600


Script:

#!/bin/bash
# Check encryption patterns and key usage in context
rg -B 3 -A 3 "encrypt_to_bytes"

# Look for conversation key derivation patterns
rg -B 2 -A 2 "ConversationKey::derive"

# Check the entire gift wrap event creation context
ast-grep --pattern 'fn create_gift_wrap($$$) {
  $$$
}'

Length of output: 2680

src/flow.rs (2)

3-3: LGTM: Import update aligns with the new message structure.

The change from Content to Payload is consistent with the broader refactoring of message handling across the codebase.


67-67: Verify message handling compatibility.

The migration from Content::Order to Payload::Order appears correct, but we should verify that all consumers of these messages handle the new payload type correctly.

Also applies to: 77-77, 94-94

✅ Verification successful

Based on the search results from our verification attempts, we can see that:

  1. The codebase shows consistent usage of Payload::Order in multiple locations (src/flow.rs, src/app/add_invoice.rs)
  2. There are no remaining instances of Content::Order found
  3. The message handling patterns show proper handling of order-related payloads across different modules
  4. The migration appears to be complete and consistent throughout the codebase

Migration appears correct and complete.

The change from Content::Order to Payload::Order has been properly implemented across the codebase. All consumers are using the new Payload::Order type consistently, and there are no remaining instances of the old Content::Order type.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining Content type usage that might need updating
# and verify that all message handlers support the new Payload type.

# Search for any remaining Content type usage
rg -l "Content::" 

# Search for Payload message handlers
ast-grep --pattern 'match $msg {
  Payload::$_ => $$$
}'

Length of output: 76


Script:

#!/bin/bash
# Let's try a more comprehensive search approach

# Search for Content enum definition and usage
rg -A 5 "enum Content"

# Search for Payload enum definition and usage
rg -A 5 "enum Payload"

# Search for Order type handling in general
rg -A 5 "Order"

# Search for message handling patterns
rg -A 5 "match.*message"
rg -A 5 "match.*msg"

Length of output: 37292


Script:

#!/bin/bash
# Let's search for Payload enum definition and usage patterns
ast-grep --pattern 'enum Payload {
  $$$
}'

# Search for message handling with Payload
ast-grep --pattern 'match $_ {
  Payload::Order($_) => $$$
}'

# Search for specific Content to Payload migration patterns
rg -A 3 "Content::Order" 

Length of output: 134

src/app/take_sell.rs (2)

75-77: LGTM: Enhanced error handling with specific reason.

The use of CantDoReason::InvalidInvoice provides more precise error feedback compared to the previous generic error message.


129-129: Verify trade index initialization and usage.

The addition of trade index tracking is good, but we should ensure it's properly initialized and consistently used throughout the order lifecycle.

✅ Verification successful

Based on the search results, I can now generate the final response as we have comprehensive information about trade index handling across the codebase.

Trade index initialization and usage is properly implemented

The trade index is consistently handled throughout the order lifecycle:

  • Properly initialized in database schemas for both orders and users tables with default value 0
  • Correctly tracked and updated during order creation and taking processes
  • Verified through check_trade_index function before processing orders
  • Safely propagated through message passing system with proper null handling (Option)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify trade index handling across the codebase

# Check for trade index field usage
rg "trade_index" -A 3 -B 3

# Check for potential null/undefined handling
ast-grep --pattern 'trade_index = $expr'

Length of output: 10555

src/app/admin_settle.rs (2)

36-36: LGTM: Good refactoring to reduce redundant calls.

Extracting inner_message improves code readability and reduces repeated calls to get_inner_message_kind().


46-46: Verify consistent trade index propagation.

The trade index is consistently passed through the message chain, but we should verify that it's properly propagated in all possible code paths.

Also applies to: 71-71, 89-89, 149-149

✅ Verification successful

Trade index propagation is properly implemented and verified

Based on the code analysis, the trade index is consistently propagated throughout the system:

  • Properly stored in the database schema for both users (trade_index) and orders (trade_index_buyer, trade_index_seller)
  • Correctly handled during message creation via Message::new_order
  • Verified during message processing through check_trade_index
  • Consistently propagated in all administrative actions (settle, cancel)
  • Properly updated during order lifecycle (new order, take buy, take sell)

The implementation includes proper validation and update mechanisms, with the trade index being checked against the user's current index and updated when necessary.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify trade index propagation in message handling

# Check message creation patterns
ast-grep --pattern 'Message::new_order($$$, $trade_index, $$$)'

# Check for potential missing trade index in message handlers
rg "send_new_order_msg.*None" -A 2

Length of output: 102


Script:

#!/bin/bash
# Let's try a broader search for trade_index usage and message handling

# Search for trade_index field or parameter
rg "trade_index" -A 2

# Search for Message struct definition and its implementations
ast-grep --pattern 'struct Message {
  $$$
}'

# Search for message handling functions
ast-grep --pattern 'fn handle_$_($$$) {
  $$$
}'

# Search for new_order message construction
rg "new_order.*trade" -A 2

Length of output: 6073

src/app/admin_cancel.rs (2)

67-69: LGTM! Message construction is properly structured.

The message construction correctly includes all necessary parameters in the right order.


82-87: Changes are consistent with earlier modifications.

src/app/rate_user.rs (2)

5-5: Import changes align with the new message structure.


174-176: LGTM! Message structure is correctly updated.

The transition to Payload and the addition of trade_index parameter are consistent with the new message structure.

src/app/add_invoice.rs (1)

6-6: Import changes are consistent with the new message structure.

src/app/admin_take_dispute.rs (3)

7-7: LGTM: Message type update aligns with the broader refactoring.

The change from Content to Payload is consistent with the codebase-wide message handling improvements.


125-128: LGTM: Consistent message construction pattern.

The message construction for buyer and seller notifications follows the same pattern as the admin notification.

Also applies to: 133-136


66-66: Verify message construction changes across the codebase.

The Message constructor API has changed with reordered parameters and new optional parameters.

Also applies to: 112-115

✅ Verification successful

Let me gather more information about the Message struct definition to understand the constructor parameters.


Let me try a different approach to find the Message struct and its methods.


Based on the results from our previous searches, I can make a determination. Looking at all the usages of Message::new_dispute and Message::new_order across the codebase, we can see that all instances follow the same parameter pattern as the changes in admin_take_dispute.rs. For example:

  • In admin_add_solver.rs, Message::new_dispute is called with (None, request_id, None, Action::AdminAddSolver, None)
  • In admin_settle.rs and admin_cancel.rs, Message::new_order is called with similar parameter structure including optional parameters
  • All other usages in admin_take_dispute.rs follow the same pattern

No inconsistencies found in Message constructor usage

The Message constructor API appears to be used consistently across the codebase with the same parameter ordering and optional parameters pattern.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other Message::new_dispute calls that might need updates
ast-grep --pattern 'Message::new_dispute($$$)'

# Search for other Message::new_order calls that might need updates
ast-grep --pattern 'Message::new_order($$$)'

Length of output: 2340


Script:

#!/bin/bash
# Search for Message struct definition and its constructors
ast-grep --pattern 'impl Message {
  $$$
  fn new_dispute($$$) {
    $$$
  }
  $$$
}'

ast-grep --pattern 'impl Message {
  $$$
  fn new_order($$$) {
    $$$
  }
  $$$
}'

Length of output: 173


Script:

#!/bin/bash
# Search for Message struct and its methods using ripgrep
rg "impl Message" -A 10

# Search for new_dispute and new_order function definitions
rg "fn new_(dispute|order)" -A 5

Length of output: 60

src/nip33.rs (1)

32-32: LGTM: Improved readability in event construction.

The separation of tags construction from the event builder chain makes the code more readable and maintainable.

Also applies to: 34-35

src/app/dispute.rs (2)

14-14: LGTM: Import updated to use Payload

The change from Content to Payload aligns with the new message structure requirements.


243-245: LGTM: Secure dispute token handling

The implementation correctly:

  • Generates unique tokens for both parties
  • Uses secure random number generation
  • Properly structures the dispute payload

Also applies to: 261-263

src/app/release.rs (1)

Line range hint 251-267: LGTM: Robust payment success handling

The implementation correctly handles all aspects of successful payment completion:

  • Proper notification to all parties
  • Order status updates
  • Range order processing
src/scheduler.rs (2)

Line range hint 1-1: Consider additional key management requirements

Based on the PR objectives, which mention signature verification for new-order, take-sell, and take-buy messages, this file might need additional changes to support the new key management system.

Consider:

  1. Adding signature verification in relevant job handlers
  2. Implementing handling for CantDo messages when signature verification fails
  3. Ensuring proper key rotation and management in the scheduler jobs

Let's check if these message types are handled elsewhere:


53-54: 🛠️ Refactor suggestion

Improve relay list event creation and error handling

The current implementation has several issues that should be addressed:

  1. The empty string content parameter is unnecessary for NIP-65 relay list events
  2. Error handling could be more specific
  3. The implementation doesn't align with the new key management system described in the PR objectives

Consider applying this refactor:

-                if let Ok(relay_ev) =
-                    EventBuilder::new(NostrKind::RelayList, "").sign_with_keys(&mostro_keys)
+                let relay_ev = match EventBuilder::new(NostrKind::RelayList, relay_tags)
+                    .sign_with_keys(&mostro_keys)
+                {
+                    Ok(ev) => ev,
+                    Err(e) => {
+                        error!("Failed to create relay list event: {}", e);
+                        continue;
+                    }
+                };

Let's verify the usage of EventBuilder across the codebase to ensure consistency:

migrations/20231005195154_users.sql Outdated Show resolved Hide resolved
Comment on lines +41 to +43
let trade_index = inner_message.trade_index.unwrap_or(0);
let public_key = PublicKey::from_bech32(npubkey)?.to_hex();
let user = User::new(public_key, 0, 1, 0, 0);
let user = User::new(public_key, 0, 1, 0, 0, trade_index);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for trade_index value

The trade_index is unwrapped with a default of 0, but there's no validation of the provided value.

-    let trade_index = inner_message.trade_index.unwrap_or(0);
+    let trade_index = match inner_message.trade_index {
+        Some(index) if index >= 0 => index,
+        Some(_) => {
+            error!("Invalid negative trade_index provided");
+            return Ok(());
+        }
+        None => 0,
+    };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let trade_index = inner_message.trade_index.unwrap_or(0);
let public_key = PublicKey::from_bech32(npubkey)?.to_hex();
let user = User::new(public_key, 0, 1, 0, 0);
let user = User::new(public_key, 0, 1, 0, 0, trade_index);
let trade_index = match inner_message.trade_index {
Some(index) if index >= 0 => index,
Some(_) => {
error!("Invalid negative trade_index provided");
return Ok(());
}
None => 0,
};
let public_key = PublicKey::from_bech32(npubkey)?.to_hex();
let user = User::new(public_key, 0, 1, 0, 0, trade_index);

Comment on lines +25 to +31
// We convert back the string to a message
let message = Message::from_json(&payload).unwrap();
// We sign the message
let sig = message.get_inner_message_kind().sign(sender_keys);
// We compose the content
let content = (message, sig);
let content = serde_json::to_string(&content).unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: Ensure message signature verification

The implementation signs messages but needs proper error handling for signature operations.

Add error handling for message parsing and signing:

-    let message = Message::from_json(&payload).unwrap();
-    let sig = message.get_inner_message_kind().sign(sender_keys);
-    let content = (message, sig);
-    let content = serde_json::to_string(&content).unwrap();
+    let message = Message::from_json(&payload)
+        .map_err(|e| BuilderError::Custom(format!("Failed to parse message: {}", e)))?;
+    let sig = message.get_inner_message_kind().sign(sender_keys);
+    let content = (message, sig);
+    let content = serde_json::to_string(&content)
+        .map_err(|e| BuilderError::Custom(format!("Failed to serialize content: {}", e)))?;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// We convert back the string to a message
let message = Message::from_json(&payload).unwrap();
// We sign the message
let sig = message.get_inner_message_kind().sign(sender_keys);
// We compose the content
let content = (message, sig);
let content = serde_json::to_string(&content).unwrap();
// We convert back the string to a message
let message = Message::from_json(&payload)
.map_err(|e| BuilderError::Custom(format!("Failed to parse message: {}", e)))?;
// We sign the message
let sig = message.get_inner_message_kind().sign(sender_keys);
// We compose the content
let content = (message, sig);
let content = serde_json::to_string(&content)
.map_err(|e| BuilderError::Custom(format!("Failed to serialize content: {}", e)))?;

Comment on lines +34 to +44
let inner_message = msg.get_inner_message_kind();

match is_assigned_solver(pool, &event.sender.to_string(), order_id).await {
Ok(false) => {
send_new_order_msg(
msg.get_inner_message_kind().request_id,
inner_message.request_id,
Some(order_id),
Action::IsNotYourDispute,
None,
&event.sender,
inner_message.trade_index,
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Inconsistent trade_index usage detected in message handling

The verification reveals that while send_new_order_msg function properly defines the trade_index parameter, the previous grep results showed multiple call sites without the trade_index parameter visible in the context. This suggests potential inconsistencies in how trade_index is being passed.

  • src/util.rs: Function definition shows trade_index is an Option<i64> parameter
  • Multiple call sites across different modules (src/app/*) need to be checked for proper trade_index passing
🔗 Analysis chain

LGTM! Verify trade_index usage across the codebase.

The refactoring improves code maintainability by reducing repeated calls to get_inner_message_kind(). The consistent passing of trade_index enhances message tracking.


Let me gather more information to verify the trade_index parameter usage.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify consistent trade_index usage in message handling
# Test: Search for send_new_order_msg calls to ensure trade_index is consistently passed
rg -A 2 "send_new_order_msg\(" | grep -v "trade_index"

Length of output: 7444


Script:

#!/bin/bash
# Check the function definition and all usages of send_new_order_msg
ast-grep --pattern 'pub async fn send_new_order_msg($$$)'

Length of output: 59


Script:

#!/bin/bash
# Let's try with ripgrep to find the function definition and its parameters
rg -A 10 "^pub async fn send_new_order_msg"

Length of output: 556

src/db.rs Outdated Show resolved Hide resolved
Copy link
Collaborator

@Catrya Catrya left a comment

Choose a reason for hiding this comment

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

Hi @arkanoider @grunch this is what appears in the mostrod logs when I try to create a new order from mostro-cli the latest version

2024-12-11T16:05:36.826190Z  WARN mostrod::app: Failed to parse event message from JSON: expected value at line 1 column 1

and this is what mostrod receives

2024-12-11T16:05:36.826142Z  INFO mostrod::app: Raw event content: "[{\"order\":{\"version\":1,\"request_id\":null,\"trade_index\":1,\"action\":\"new-order\",\"payload\":{\"order\":{\"kind\":\"sell\",\"status\":\"pending\",\"amount\":0,\"fiat_code\":\"CUP\",\"min_amount\":null,\"max_amount\":null,\"fiat_amount\":400,\"payment_method\":\"test11dic1105\",\"premium\":0,\"created_at\":0,\"expires_at\":null,\"buyer_token\":null,\"seller_token\":null}}}},\"9b1365d4530dc14846a87306e6892793b65ba4df3333fb90f35c7f9b8f444e53e4cd42d6c816dbedee3eec20bff758c86b4d13ca9d13ccf91e84d65a7055a23d\"]"

Cargo.toml Outdated Show resolved Hide resolved
Cargo.toml Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (5)
src/main.rs (1)

113-114: Enhance test coverage for message handling

While the tests cover basic serialization and validation, consider adding:

  1. Test cases for invalid trade indices
  2. Tests for signature verification failures
  3. Edge cases with malformed payloads
+    #[test]
+    fn test_invalid_trade_index() {
+        let sample_message = r#"{"order":{"version":1,"request_id":1,"trade_index":-1,"id":"7dd204d2-d06c-4406-a3d9-4415f4a8b9c9","action":"fiat-sent","payload":null}}"#;
+        let message: Message = serde_json::from_str(sample_message).unwrap();
+        assert!(!message.verify());
+    }

Also applies to: 122-123

src/util.rs (4)

167-209: Consider adding validation for trade_index.

While the addition of trade_index parameter enhances order tracking, there's no validation to ensure its consistency or format.

Consider adding validation like:

 pub async fn publish_order(
     pool: &SqlitePool,
     keys: &Keys,
     new_order: &SmallOrder,
     initiator_pubkey: &str,
     ack_pubkey: PublicKey,
     request_id: Option<u64>,
     trade_index: Option<i64>,
 ) -> Result<()> {
+    if let Some(index) = trade_index {
+        if index < 0 {
+            return Err(anyhow::anyhow!("Invalid trade index"));
+        }
+    }
     // Prepare a new default order
     let new_order_db = prepare_new_order(new_order, initiator_pubkey, trade_index).await?;

Line range hint 422-438: Consider consolidating message payload construction.

The message payload construction is spread across multiple locations. This could be refactored into a dedicated function for better maintainability.

Consider creating a helper function:

fn create_payment_request_payload(
    order: Option<SmallOrder>,
    payment_request: String,
    trade_index: Option<i64>
) -> Payload {
    Payload::PaymentRequest(order, payment_request, None)
}

Line range hint 618-626: Enhance error handling in send_cant_do_msg.

The function silently ignores errors from send_dm. Consider logging these errors or propagating them to the caller.

 pub async fn send_cant_do_msg(
     request_id: Option<u64>,
     order_id: Option<Uuid>,
     reason: Option<CantDoReason>,
     destination_key: &PublicKey,
-) {
+) -> Result<()> {
     let message = Message::cant_do(order_id, request_id, Some(Payload::CantDo(reason)));
     if let Ok(message) = message.as_json() {
         let sender_keys = crate::util::get_keys().unwrap();
-        let _ = send_dm(destination_key, sender_keys, message).await;
+        send_dm(destination_key, sender_keys, message).await?;
     }
+    Ok(())
 }

Line range hint 753-782: Update test cases for new message format.

The test cases have been updated to use the new Payload type, but they could be more comprehensive.

Consider adding test cases for:

  • Different CantDoReason variants
  • Edge cases with trade_index
  • Error scenarios in message handling
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 31f8ee3 and 7c7fd6e.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • Cargo.toml (2 hunks)
  • migrations/20221222153301_orders.sql (1 hunks)
  • migrations/20231005195154_users.sql (1 hunks)
  • src/app.rs (2 hunks)
  • src/main.rs (1 hunks)
  • src/nip59.rs (4 hunks)
  • src/util.rs (12 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • migrations/20231005195154_users.sql
  • Cargo.toml
  • migrations/20221222153301_orders.sql
🔇 Additional comments (6)
src/nip59.rs (3)

52-52: LGTM: Code simplification

Removing the empty vector argument from EventBuilder::new is a good cleanup that maintains the same functionality.


23-34: ⚠️ Potential issue

Critical: Add proper error handling to prevent panics

The function contains multiple unwrap() calls that could lead to runtime panics. This is particularly risky in a cryptographic context where message parsing or signing could fail.

Consider applying this fix:

-    let message: Message = serde_json::from_str(&payload).unwrap();
-    let sig = message.get_inner_message_kind().sign(sender_keys);
-    let content = (message, sig);
-    let content = serde_json::to_string(&content).unwrap();
+    let message: Message = serde_json::from_str(&payload)
+        .map_err(|e| BuilderError::Custom(format!("Failed to parse message: {}", e)))?;
+    let sig = message.get_inner_message_kind().sign(sender_keys);
+    let content = (message, sig);
+    let content = serde_json::to_string(&content)
+        .map_err(|e| BuilderError::Custom(format!("Failed to serialize content: {}", e)))?;

Likely invalid or redundant comment.


73-82: Verify tag construction and event building

While the changes improve readability, please ensure that:

  1. The Tags::new constructor properly handles empty or invalid tags
  2. The event building chain maintains the same security properties as before
✅ Verification successful

Let me gather more information about the event building chain to ensure security properties.


Let me check one final aspect about the security properties by looking at the NIP-59 implementation.


Changes are safe and consistent with codebase patterns

Based on the verification results:

  1. Tags::new is consistently used across the codebase with properly structured tag vectors, including in similar security-sensitive contexts
  2. The event building chain maintains the same security properties:
    • Uses the correct Kind::GiftWrap
    • Applies the required random timestamp tweak (RANGE_RANDOM_TIMESTAMP_TWEAK)
    • Properly signs with ephemeral keys
    • Follows the same pattern as other secure event constructions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other usages of Tags::new to verify consistent handling
ast-grep --pattern 'Tags::new($_)'

Length of output: 6971


Script:

#!/bin/bash
# Search for similar event building patterns to verify security properties
ast-grep --pattern 'EventBuilder::new($_, $_)
        .tags($_)
        .custom_created_at($_)
        .build($_)
        .sign_with_keys($_)'

Length of output: 485


Script:

#!/bin/bash
# Check NIP-59 implementation for security requirements
rg -A 5 "RANGE_RANDOM_TIMESTAMP_TWEAK" src/

Length of output: 550

src/app.rs (1)

212-223: Verify message parsing and trade index checks

The message parsing and trade index verification are critical security checks. Ensure that:

  1. Message parsing failures are properly logged
  2. Trade index verification cannot be bypassed
  3. Race conditions in trade index updates are handled
✅ Verification successful

Trade index verification is properly implemented and secure

Based on the code analysis, the trade index verification is well-implemented with multiple security measures:

  1. Trade indices are properly tracked per user in the database
  2. Index verification includes signature validation to prevent tampering
  3. Race conditions are handled by atomic updates in the database
  4. Trade index checks are consistently applied across all relevant message types (NewOrder, TakeBuy, TakeSell)
  5. Failed message parsing is properly logged and skipped
  6. The implementation prevents index manipulation by:
    • Verifying signatures
    • Only accepting higher indices
    • Requiring valid message structure
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other trade index verifications to ensure consistent handling
ast-grep --pattern 'has_trade_index()'

Length of output: 40


Script:

#!/bin/bash
# Search for trade index related functions and checks
rg -A 5 "check_trade_index|trade_index" 

# Search for Message struct and its implementations
ast-grep --pattern 'struct Message'

# Search for message kind related code
rg -A 3 "get_inner_message_kind"

# Look for error handling patterns around message parsing
rg -A 3 "Failed to parse.*message"

Length of output: 19454

src/util.rs (2)

17-18: LGTM: Import changes align with the new message handling system.

The migration from Content to Payload type and addition of CantDoReason improve error handling specificity.


247-250: Verify trade index assignment logic.

The trade index assignment differs between buyer and seller. Ensure this asymmetric assignment is intentional and documented.

✅ Verification successful

Let me gather more context about the order struct and its usage.


Based on the gathered context, I can now provide the final response:

The trade index assignment logic is verified and intentionally asymmetric.

The asymmetric assignment is part of a well-designed trade tracking system where:

  • For buy orders: trade_index_buyer is set during order creation or take_sell action
  • For sell orders: trade_index_seller is set during order creation or take_buy action

This is consistent with the database schema (migrations/20221222153301_orders.sql) and is used in conjunction with the user's trade index tracking (migrations/20231005195154_users.sql) for verification purposes in check_trade_index.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other occurrences of trade_index assignment to verify consistency
rg -A 2 "trade_index_(buyer|seller)\s*=" 

Length of output: 766


Script:

#!/bin/bash
# Search for the Order struct definition
ast-grep --pattern 'struct Order'

# Look for documentation or comments around trade_index fields
rg -B 3 "trade_index"

Length of output: 7672

src/app.rs Outdated
Comment on lines 74 to 75
let (_, sig): (String, nostr_sdk::secp256k1::schnorr::Signature) =
serde_json::from_str(&event.rumor.content).unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: Add error handling for JSON deserialization

The unwrap call could lead to runtime panics if the content is malformed.

-    let (_, sig): (String, nostr_sdk::secp256k1::schnorr::Signature) =
-        serde_json::from_str(&event.rumor.content).unwrap();
+    let (_, sig): (String, nostr_sdk::secp256k1::schnorr::Signature) = serde_json::from_str(&event.rumor.content)
+        .map_err(|e| {
+            tracing::error!("Failed to parse signature: {}", e);
+            return;
+        })?;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let (_, sig): (String, nostr_sdk::secp256k1::schnorr::Signature) =
serde_json::from_str(&event.rumor.content).unwrap();
let (_, sig): (String, nostr_sdk::secp256k1::schnorr::Signature) = serde_json::from_str(&event.rumor.content)
.map_err(|e| {
tracing::error!("Failed to parse signature: {}", e);
return;
})?;

src/app.rs Outdated
Comment on lines 55 to 111
/// Function to check if a user is present in the database and update or create their trade index.
///
/// This function performs the following tasks:
/// 1. It checks if the action associated with the incoming message is related to trading (NewOrder, TakeBuy, or TakeSell).
/// 2. If the user is found in the database, it verifies the trade index and the signature of the message.
/// - If valid, it updates the user's trade index.
/// - If invalid, it logs a warning and sends a message indicating the issue.
/// 3. If the user is not found, it creates a new user entry with the provided trade index if applicable.
///
/// # Arguments
/// * `pool` - The database connection pool used to query and update user data.
/// * `event` - The unwrapped gift event containing the sender's information.
/// * `msg` - The message containing action details and trade index information.
async fn check_trade_index(pool: &Pool<Sqlite>, event: &UnwrappedGift, msg: &Message) {
let message_kind = msg.get_inner_message_kind();
if let Action::NewOrder | Action::TakeBuy | Action::TakeSell = message_kind.action {
match is_user_present(pool, event.sender.to_string()).await {
Ok(mut user) => {
if let (true, index) = message_kind.has_trade_index() {
let (_, sig): (String, nostr_sdk::secp256k1::schnorr::Signature) =
serde_json::from_str(&event.rumor.content).unwrap();
if index > user.trade_index
&& msg
.get_inner_message_kind()
.verify_signature(event.sender, sig)
{
user.trade_index = index;
if user.update(pool).await.is_ok() {
tracing::info!("Update user trade index");
}
} else {
tracing::info!("Invalid signature or trade index");
send_cant_do_msg(
None,
msg.get_inner_message_kind().id,
Some(CantDoReason::InvalidTradeIndex),
&event.sender,
)
.await;
}
}
}
Err(_) => {
if let (true, trade_index) = message_kind.has_trade_index() {
let new_user = User {
pubkey: event.sender.to_string(),
trade_index,
..Default::default()
};

match new_user.create(pool).await {
Ok(_) => tracing::info!("Added new user for rate"),
Err(e) => tracing::error!("Error creating user: {:#?}", e),
}
}
}
}
}
}
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 11, 2024

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor complex function for better maintainability

The check_trade_index function handles multiple responsibilities and could benefit from being split into smaller, focused functions.

Consider breaking it down into:

  1. validate_trade_index
  2. update_user_trade_index
  3. create_new_user_with_trade_index
+async fn validate_trade_index(index: i64, user: &User, signature: &nostr_sdk::secp256k1::schnorr::Signature, sender: &XOnlyPublicKey, msg: &Message) -> bool {
+    index > user.trade_index && msg.get_inner_message_kind().verify_signature(sender, signature)
+}

+async fn update_user_trade_index(pool: &Pool<Sqlite>, mut user: User, index: i64) -> Result<(), sqlx::Error> {
+    user.trade_index = index;
+    user.update(pool).await
+}

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Member Author

Choose a reason for hiding this comment

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

agree with the 🐰

Copy link
Contributor

Choose a reason for hiding this comment

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

@grunch, glad to hear you agree!

Here's how you might refactor the check_trade_index function for better maintainability:

First, define the helper functions:

async fn validate_trade_index(
    index: i64,
    user: &User,
    signature: &nostr_sdk::secp256k1::schnorr::Signature,
    sender: &XOnlyPublicKey,
    msg: &Message,
) -> bool {
    index > user.trade_index && msg.get_inner_message_kind().verify_signature(sender, *signature)
}

async fn update_user_trade_index(
    pool: &Pool<Sqlite>,
    mut user: User,
    index: i64,
) -> Result<(), sqlx::Error> {
    user.trade_index = index;
    user.update(pool).await
}

async fn create_new_user_with_trade_index(
    pool: &Pool<Sqlite>,
    sender_pubkey: &str,
    trade_index: i64,
) -> Result<(), sqlx::Error> {
    let new_user = User {
        pubkey: sender_pubkey.to_string(),
        trade_index,
        ..Default::default()
    };
    new_user.create(pool).await
}

Then, refactor the check_trade_index function to utilize these helpers:

async fn check_trade_index(pool: &Pool<Sqlite>, event: &UnwrappedGift, msg: &Message) {
    let message_kind = msg.get_inner_message_kind();
    if let Action::NewOrder | Action::TakeBuy | Action::TakeSell = message_kind.action {
        if let (true, index) = message_kind.has_trade_index() {
            match is_user_present(pool, event.sender.to_string()).await {
                Ok(mut user) => {
                    let (_, sig): (String, nostr_sdk::secp256k1::schnorr::Signature) =
                        serde_json::from_str(&event.rumor.content).unwrap();
                    if validate_trade_index(index, &user, &sig, &event.sender, msg).await {
                        if let Err(e) = update_user_trade_index(pool, user, index).await {
                            tracing::error!("Error updating user: {:#?}", e);
                        } else {
                            tracing::info!("Updated user trade index");
                        }
                    } else {
                        tracing::info!("Invalid signature or trade index");
                        send_cant_do_msg(
                            None,
                            message_kind.id,
                            Some(CantDoReason::InvalidTradeIndex),
                            &event.sender,
                        )
                        .await;
                    }
                }
                Err(_) => {
                    if let Err(e) =
                        create_new_user_with_trade_index(pool, &event.sender.to_string(), index)
                            .await
                    {
                        tracing::error!("Error creating user: {:#?}", e);
                    } else {
                        tracing::info!("Added new user for rate");
                    }
                }
            }
        }
    }
}

This refactoring breaks down check_trade_index into smaller, focused functions:

  • validate_trade_index: Validates the trade index and signature.
  • update_user_trade_index: Updates the user's trade index in the database.
  • create_new_user_with_trade_index: Creates a new user with the provided trade index.

This approach improves code readability and maintainability by separating concerns and making each function responsible for a single task.

Copy link
Member Author

Choose a reason for hiding this comment

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

@arkanoider please give a look to this 😃

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

src/app.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (2)
src/app.rs (2)

209-215: Refactor: Extract message verification logic

The message verification logic could be extracted into a separate function to improve readability and reusability.

Suggested refactor:

+async fn verify_message(event: &UnwrappedGift) -> Option<(Message, Signature)> {
+    match serde_json::from_str(&event.rumor.content) {
+        Ok((message, sig)) => {
+            let inner_message = message.get_inner_message_kind();
+            if !inner_message.verify_signature(event.rumor.pubkey, sig) {
+                tracing::warn!("Error in event verification");
+                return None;
+            }
+            Some((message, sig))
+        }
+        Err(e) => {
+            tracing::error!("Failed to parse message: {}", e);
+            None
+        }
+    }
+}

-    let (message, sig): (Message, Signature) =
-        serde_json::from_str(&event.rumor.content).unwrap();
-    let inner_message = message.get_inner_message_kind();
-    if !inner_message.verify_signature(event.rumor.pubkey, sig) {
-        tracing::warn!("Error in event verification");
-        continue;
-    }
+    let (message, sig) = match verify_message(&event).await {
+        Some(result) => result,
+        None => continue,
+    };
+    let inner_message = message.get_inner_message_kind();

Also applies to: 217-218


55-67: Add documentation for error cases

While the function is well-documented for the happy path, it's missing documentation about error cases and the conditions under which CantDo messages are sent.

Add error cases to the documentation:

 /// Function to check if a user is present in the database and update or create their trade index.
 ///
 /// This function performs the following tasks:
 /// 1. It checks if the action associated with the incoming message is related to trading (NewOrder, TakeBuy, or TakeSell).
 /// 2. If the user is found in the database, it verifies the trade index and the signature of the message.
 ///    - If valid, it updates the user's trade index.
 ///    - If invalid, it logs a warning and sends a message indicating the issue.
 /// 3. If the user is not found, it creates a new user entry with the provided trade index if applicable.
+///
+/// # Error Handling
+/// The function sends CantDo messages in the following cases:
+/// - Invalid trade index (when the new index is not greater than the current one)
+/// - Invalid signature (when message signature verification fails)
+/// - Invalid message format (when message deserialization fails)
 ///
 /// # Arguments
 /// * `pool` - The database connection pool used to query and update user data.
 /// * `event` - The unwrapped gift event containing the sender's information.
 /// * `msg` - The message containing action details and trade index information.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7c7fd6e and df2c53b.

📒 Files selected for processing (1)
  • src/app.rs (2 hunks)
🔇 Additional comments (4)
src/app.rs (4)

37-37: LGTM: Import changes are well-structured

The new imports are properly organized and align with the added functionality for trade index verification and user management.

Also applies to: 41-41, 43-44, 47-47


74-75: ⚠️ Potential issue

Critical: Replace unwrap() with proper error handling

The JSON deserialization could panic if the content is malformed. This is a critical reliability issue.

Apply this fix:

-    let (_, sig): (String, nostr_sdk::secp256k1::schnorr::Signature) =
-        serde_json::from_str(&event.rumor.content).unwrap();
+    let (_, sig): (String, nostr_sdk::secp256k1::schnorr::Signature) = match serde_json::from_str(&event.rumor.content) {
+        Ok(result) => result,
+        Err(e) => {
+            tracing::error!("Failed to parse signature: {}", e);
+            send_cant_do_msg(
+                None,
+                msg.get_inner_message_kind().id,
+                Some(CantDoReason::InvalidSignature),
+                &event.sender,
+            )
+            .await;
+            return;
+        }
+    };

209-215: ⚠️ Potential issue

Critical: Add error handling for message deserialization

Similar to the previous issue, using unwrap() here could cause runtime panics.

Apply this fix:

-    let (message, sig): (Message, Signature) =
-        serde_json::from_str(&event.rumor.content).unwrap();
+    let (message, sig): (Message, Signature) = match serde_json::from_str(&event.rumor.content) {
+        Ok(result) => result,
+        Err(e) => {
+            tracing::error!("Failed to parse message: {}", e);
+            continue;
+        }
+    };

Likely invalid or redundant comment.


68-111: 🛠️ Refactor suggestion

Refactor: Split complex function into smaller units

The check_trade_index function has multiple responsibilities and deep nesting, making it harder to maintain and test. Consider breaking it down into smaller, focused functions.

Suggested refactor:

+async fn validate_trade_index(
+    index: i64,
+    user: &User,
+    sender: &XOnlyPublicKey,
+    sig: nostr_sdk::secp256k1::schnorr::Signature,
+    msg: &Message,
+) -> bool {
+    index > user.trade_index && msg.get_inner_message_kind().verify_signature(sender, sig)
+}
+
+async fn update_user_trade_index(
+    pool: &Pool<Sqlite>,
+    user: &mut User,
+    index: i64,
+) -> Result<(), sqlx::Error> {
+    user.trade_index = index;
+    user.update(pool).await
+}
+
+async fn create_new_user(
+    pool: &Pool<Sqlite>,
+    sender: String,
+    trade_index: i64,
+) -> Result<(), sqlx::Error> {
+    let new_user = User {
+        pubkey: sender,
+        trade_index,
+        ..Default::default()
+    };
+    new_user.create(pool).await
+}
+
 async fn check_trade_index(pool: &Pool<Sqlite>, event: &UnwrappedGift, msg: &Message) {
     let message_kind = msg.get_inner_message_kind();
     if let Action::NewOrder | Action::TakeBuy | Action::TakeSell = message_kind.action {
         match is_user_present(pool, event.sender.to_string()).await {
             Ok(mut user) => {
                 if let (true, index) = message_kind.has_trade_index() {
-                    let (_, sig): (String, nostr_sdk::secp256k1::schnorr::Signature) =
-                        serde_json::from_str(&event.rumor.content).unwrap();
-                    if index > user.trade_index
-                        && msg
-                            .get_inner_message_kind()
-                            .verify_signature(event.sender, sig)
-                    {
-                        user.trade_index = index;
-                        if user.update(pool).await.is_ok() {
-                            tracing::info!("Update user trade index");
-                        }
+                    let (_, sig) = match serde_json::from_str(&event.rumor.content) {
+                        Ok(result) => result,
+                        Err(e) => {
+                            tracing::error!("Failed to parse signature: {}", e);
+                            send_cant_do_msg(
+                                None,
+                                msg.get_inner_message_kind().id,
+                                Some(CantDoReason::InvalidSignature),
+                                &event.sender,
+                            )
+                            .await;
+                            return;
+                        }
+                    };
+                    
+                    if validate_trade_index(index, &user, &event.sender, sig, msg).await {
+                        if let Err(e) = update_user_trade_index(pool, &mut user, index).await {
+                            tracing::error!("Failed to update user trade index: {}", e);
+                        } else {
+                            tracing::info!("Updated user trade index");
+                        }
                     } else {
                         tracing::info!("Invalid signature or trade index");
                         send_cant_do_msg(
                            None,
                            msg.get_inner_message_kind().id,
                            Some(CantDoReason::InvalidTradeIndex),
                            &event.sender,
                        )
                        .await;
                    }
                 }
             }
             Err(_) => {
                 if let (true, trade_index) = message_kind.has_trade_index() {
-                    let new_user = User {
-                        pubkey: event.sender.to_string(),
-                        trade_index,
-                        ..Default::default()
-                    };
-                    if new_user.create(pool).await.is_ok() {
-                        tracing::info!("Added new user for rate");
+                    if let Err(e) = create_new_user(pool, event.sender.to_string(), trade_index).await {
+                        tracing::error!("Failed to create new user: {}", e);
+                    } else {
+                        tracing::info!("Added new user");
                     }
                 }
             }
         }
     }
 }

Likely invalid or redundant comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (2)
src/db.rs (2)

314-328: Consider renaming the function to better reflect its behavior.

The function name is_user_present suggests a boolean return, but it actually returns the full User object. Consider renaming it to get_user_by_pubkey or find_user to better reflect its behavior.

-pub async fn is_user_present(pool: &SqlitePool, public_key: String) -> anyhow::Result<User> {
+/// Retrieves a user by their public key
+pub async fn get_user_by_pubkey(pool: &SqlitePool, public_key: String) -> anyhow::Result<User> {

Line range hint 279-293: Fix invalid SQL syntax in query conditions.

There are syntax errors in the WHERE clauses that will cause query failures:

-          WHERE invoice_held_at !=0 true AND  status == 'active'
+          WHERE invoice_held_at != 0 AND status == 'active'
-          WHERE failed_payment == true AND  status == 'settled-hold-invoice'
+          WHERE failed_payment == true AND status == 'settled-hold-invoice'
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between df2c53b and 8eafef6.

📒 Files selected for processing (1)
  • src/db.rs (2 hunks)
🔇 Additional comments (2)
src/db.rs (2)

304-304: LGTM! Good optimization.

Adding LIMIT 1 is a good optimization since we only need one result, preventing unnecessary table scanning.


Line range hint 279-328: Verify the usage of these database queries.

Let's verify how these queries are used across the codebase to ensure all callers handle potential errors correctly.

✅ Verification successful

Let me gather more information about the error handling patterns and the Order struct to ensure completeness of the verification.


Based on the gathered information, I can now generate the final response:

Database queries are properly handled across the codebase

The verification shows that:

  • All database queries return anyhow::Result and callers properly handle potential errors using if let Ok(...) pattern
  • Query usage is consistent in both scheduler and main application logic
  • Error cases are appropriately handled in all identified usage locations:
    • src/scheduler.rs: Safely handles failed payments with retry logic
    • src/main.rs: Properly handles held invoices with null safety checks
    • src/app.rs: Correctly handles user presence verification
    • src/app/admin_take_dispute.rs: Appropriately handles solver verification
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check usage of affected database queries

# Check for calls to find_held_invoices and find_failed_payment
echo "Checking query usage patterns:"
rg -A 2 "find_held_invoices|find_failed_payment"

# Check for error handling patterns around these calls
echo "Checking error handling patterns:"
rg -B 2 -A 2 "find_held_invoices\(.*\)\.await|find_failed_payment\(.*\)\.await"

Length of output: 1847


Script:

#!/bin/bash
# Check Order struct definition and its usage
echo "Checking Order struct definition:"
ast-grep --pattern 'struct Order {
  $$$
}'

# Check for any other potential error handling patterns
echo "Checking additional error handling patterns:"
rg -B 2 -A 2 "is_user_present|find_solver_pubkey"

# Check for any transaction or rollback patterns
echo "Checking transaction patterns:"
rg "begin_transaction|rollback|commit"

Length of output: 2861

Copy link
Collaborator

@Catrya Catrya left a comment

Choose a reason for hiding this comment

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

hi guys, now it says this

2024-12-11T21:39:19.904067Z  WARN mostrod::app: Error in NewOrder with context error returned from database: (code: 1) table orders has no column named trade_index_seller

Catrya

This comment was marked as duplicate.

@arkanoider
Copy link
Collaborator

hi guys, now it says this

2024-12-11T21:39:19.904067Z  WARN mostrod::app: Error in NewOrder with context error returned from database: (code: 1) table orders has no column named trade_index_seller

You have to relaunch ./init_db.sh and copy the new mostro.db in your mostro folder. We are working at least to automatize new db generation

@arkanoider
Copy link
Collaborator

hi guys, now it says this

Sorry @Catrya we need to add back the two values in db

Copy link
Collaborator

@Catrya Catrya left a comment

Choose a reason for hiding this comment

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

Creating a new order works! But:

  • When doing getdm in mostro-cli it says No new messages but I see in the mostrod logs the messages being sent, so I don't know if it's a mostrod or mostro-cli problem

  • When the user sends take-buy, mostrod responds with cant-do, mostro cli doesn't show the messages but it appears in the mostrod logs:
    2024-12-11T22:25:20.677121Z INFO mostrod::util: Sending DM, Event ID: c9c78890117ef6b0255412466adb2228cc49b9c61719774103fd1eef824c6a93 with payload: "{\"cant-do\,\"id\":\"02e688c5-1c00-46b2-87b8-9f0d44033166\",\"action\":\"cant-do\",\"payload\":{\"cant_do\":null}}}"

  • When the user sends take-sell, mostrod does nothing, nothing appears in the logs, the message being sent with mostro-cli is:

{
  "order": {
    "version": 1,
    "request_id": null,
    "trade_index": 1,
    "action": "take-sell",
    "payload": null
  }
}

( I see that because I put this in my mostro-cli:

if let Ok(json_content) = serde_json::from_str::<Value>(&payload) {
        if let Ok(pretty_content) = serde_json::to_string_pretty(&json_content){
            println!("{}", pretty_content);}
}

)

@arkanoider
Copy link
Collaborator

Creating a new order works! But:

  • When doing getdm in mostro-cli it says No new messages but I see in the mostrod logs the messages being sent, so I don't know if it's a mostrod or mostro-cli problem
  • When the user sends take-buy, mostrod responds with cant-do, mostro cli doesn't show the messages but it appears in the mostrod logs:
    2024-12-11T22:25:20.677121Z INFO mostrod::util: Sending DM, Event ID: c9c78890117ef6b0255412466adb2228cc49b9c61719774103fd1eef824c6a93 with payload: "{\"cant-do\,\"id\":\"02e688c5-1c00-46b2-87b8-9f0d44033166\",\"action\":\"cant-do\",\"payload\":{\"cant_do\":null}}}"
  • When the user sends take-sell, mostrod does nothing, nothing appears in the logs, the message being sent with mostro-cli is:
{
  "order": {
    "version": 1,
    "request_id": null,
    "trade_index": 1,
    "action": "take-sell",
    "payload": null
  }
}

( I see that because I put this in my mostro-cli:

if let Ok(json_content) = serde_json::from_str::<Value>(&payload) {
        if let Ok(pretty_content) = serde_json::to_string_pretty(&json_content){
            println!("{}", pretty_content);}
}

)

Great! Thanks @Catrya will try as soon as I am at home. Probably some of my checks for trade index are not ok now.

* bumps to mostro core v0.6.16
* add new fields on users table, fields related to user rating
* fix bug parsing message and content on app.rs
* change all dm destination, now is sending to the trade key
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (12)
migrations/20231005195154_users.sql (1)

8-8: Consider adding an index on last_trade_index

Since last_trade_index will likely be frequently queried for trade operations, consider adding an index to improve query performance.

+ CREATE INDEX idx_users_last_trade_index ON users(last_trade_index);
src/app/admin_add_solver.rs (2)

22-27: Improve error message for payload validation

The error message "No pubkey found!" doesn't clearly indicate whether the payload is missing or invalid.

-        error!("No pubkey found!");
+        error!("Invalid or missing payload in message");

50-52: Consider extracting message construction to a separate function

The message construction logic could be moved to a dedicated function for better reusability and maintainability.

+    fn create_admin_solver_message(request_id: Option<String>) -> Result<String> {
+        let message = Message::new_dispute(None, request_id, None, Action::AdminAddSolver, None);
+        message.as_json()
+    }
+
-    let message = Message::new_dispute(None, request_id, None, Action::AdminAddSolver, None);
-    let message = message.as_json()?;
+    let message = create_admin_solver_message(request_id)?;
src/app/rate_user.rs (2)

82-82: Consider adding specific error messages

While the send_cant_do_msg calls are functionally correct, consider adding more specific error messages to help with debugging:

-send_cant_do_msg(request_id, Some(order.id), None, &event.rumor.pubkey).await;
+send_cant_do_msg(request_id, Some(order.id), Some("Invalid order status for rating"), &event.rumor.pubkey).await;

Also applies to: 103-103


174-176: Consider potential race condition in rating updates

The rating update logic could be subject to race conditions if multiple ratings are processed simultaneously. Consider implementing optimistic locking:

+    // Add version field to Rating struct for optimistic locking
+    let current_version = reputation.version;
+    reputation.version += 1;
+
+    // Use a transaction to ensure atomic updates
+    let mut tx = pool.begin().await?;
+    
     update_user_rating_event(
         &counterpart,
         update_buyer_rate,
         update_seller_rate,
         reputation,
         order.id,
         my_keys,
         pool,
         rate_list,
     ).await?;
+
+    tx.commit().await?;
src/app/add_invoice.rs (1)

Line range hint 71-91: Enhance invoice validation with amount tolerance

The current invoice validation is strict about matching the exact amount. Consider adding a small tolerance for lightning network fee variations:

 match is_valid_invoice(
     payment_request.clone(),
-    Some(order.amount as u64),
+    Some((order.amount as f64 * 1.01) as u64), // Allow 1% tolerance
     Some(order.fee as u64),
 ).await
src/app/dispute.rs (1)

Line range hint 219-227: Enhance security token generation

The current implementation uses a 3-digit random number for security tokens, which might be insufficient for security purposes. Consider:

  1. Increasing the token length
  2. Using a cryptographically secure random number generator
  3. Adding expiration time for tokens
-    dispute.buyer_token = Some(rng.gen_range(100..=999));
-    dispute.seller_token = Some(rng.gen_range(100..=999));
+    use rand::rngs::OsRng;
+    use rand::distributions::Uniform;
+
+    let token_range = Uniform::new(100_000, 999_999);
+    let mut rng = OsRng;
+    dispute.buyer_token = Some(token_range.sample(&mut rng));
+    dispute.seller_token = Some(token_range.sample(&mut rng));
+    dispute.token_expiry = Some(chrono::Utc::now() + chrono::Duration::hours(24));
src/app.rs (3)

55-111: Consider refactoring for better maintainability

The function has high cyclomatic complexity with nested conditionals. Consider breaking it down into smaller, focused functions:

  1. validate_trade_index_and_signature
  2. update_user_trade_index
  3. create_new_user_with_trade_index

Also, improve error handling for database operations:

-                    if user.update(pool).await.is_ok() {
+                    match user.update(pool).await {
+                        Ok(_) => {
                             tracing::info!("Update user trade index");
+                        }
+                        Err(e) => {
+                            tracing::error!("Failed to update user trade index: {}", e);
+                        }
                     }

Line range hint 167-167: Consider adding validation for trade_index parameter

The publish_order function now accepts a trade_index parameter but doesn't validate its value. Consider adding validation to ensure the trade index is positive when provided.

+    if let Some(index) = trade_index {
+        if index < 0 {
+            return Err(anyhow::anyhow!("Trade index must be positive"));
+        }
+    }

Also applies to: 169-209


Line range hint 593-593: Improve error messaging in send_cant_do_msg calls

The send_cant_do_msg calls don't provide specific reasons for the failure. Consider adding appropriate CantDoReason values:

-    send_cant_do_msg(request_id, Some(order.id), None, &event.rumor.pubkey).await;
+    send_cant_do_msg(
+        request_id,
+        Some(order.id),
+        Some(CantDoReason::Unauthorized),
+        &event.rumor.pubkey
+    ).await;

-    send_cant_do_msg(request_id, Some(order.id), None, &event.rumor.pubkey).await;
+    send_cant_do_msg(
+        request_id,
+        Some(order.id),
+        Some(CantDoReason::InvalidPreimage),
+        &event.rumor.pubkey
+    ).await;

Also applies to: 602-602

src/util.rs (2)

167-167: Add documentation for the trade_index parameter.

The trade_index parameter has been added to both functions, but its purpose and usage should be documented. Consider adding doc comments explaining:

  • The purpose of the trade index
  • When it's expected to be Some/None
  • How it's used differently for buyers vs sellers

Also applies to: 211-215


752-763: Add test cases for error scenarios in send_dm.

The current test only covers the success path. Consider adding test cases for:

  • Invalid receiver public key
  • Failed event wrapping
  • Failed client initialization
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9fec4cf and 819670b.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • Cargo.toml (2 hunks)
  • migrations/20231005195154_users.sql (1 hunks)
  • src/app.rs (2 hunks)
  • src/app/add_invoice.rs (6 hunks)
  • src/app/admin_add_solver.rs (3 hunks)
  • src/app/admin_take_dispute.rs (5 hunks)
  • src/app/cancel.rs (11 hunks)
  • src/app/dispute.rs (5 hunks)
  • src/app/fiat_sent.rs (4 hunks)
  • src/app/order.rs (6 hunks)
  • src/app/rate_user.rs (5 hunks)
  • src/app/release.rs (5 hunks)
  • src/app/take_buy.rs (4 hunks)
  • src/app/take_sell.rs (5 hunks)
  • src/util.rs (14 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/app/fiat_sent.rs
  • src/app/take_buy.rs
  • src/app/release.rs
  • src/app/take_sell.rs
  • src/app/admin_take_dispute.rs
  • src/app/cancel.rs
  • src/app/order.rs
🔇 Additional comments (13)
migrations/20231005195154_users.sql (1)

3-3: Verify existing data before adding unique constraint

Adding a unique constraint to the existing pubkey column could fail if duplicate values exist. Ensure the migration handles existing data appropriately.

Cargo.toml (1)

20-20: Verify compatibility and security of dependency updates

Several key dependencies have been updated:

  • nostr-sdk: 0.36.0 → 0.37.0 (+ nip59 feature)
  • mostro-core: 0.6.11 → 0.6.16
  • lnurl-rs: 0.8.0 → 0.9.0
  • Added bitcoin: 0.32.5

Also applies to: 41-41, 46-46, 49-49

✅ Verification successful

Dependencies updated with minimal risk

Based on the evidence gathered:

  • nostr-sdk 0.37.0 is a feature release with NIP support additions and no breaking changes
  • The new bitcoin dependency and updated dependencies are used consistently in the codebase
  • Code changes show proper integration with bitcoin price functionality
  • The version increments are minor, suggesting backward compatibility

The dependency updates appear safe to proceed with. The nostr-sdk changelog explicitly confirms no breaking changes, and the codebase changes align with the dependency updates.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for security advisories and breaking changes
echo "Checking for security advisories..."
cargo audit

# Check compatibility of updated dependencies
echo "Checking dependency compatibility..."
cargo check

Length of output: 383


Script:

#!/bin/bash
# Let's check the git diff to understand the exact dependency changes
git diff origin/main Cargo.toml

# Also check if there are any breaking changes mentioned in the PR description or commit messages
git log --grep="BREAKING" --grep="breaking" -i HEAD^..HEAD

Length of output: 1013


Script:

#!/bin/bash
# Let's check the changelog or release notes for these dependencies in their repositories
gh api repos/rust-nostr/nostr/releases/tags/v0.37.0 || true
gh api repos/MostroP2P/mostro-core/releases/tags/v0.6.16 || true
gh api repos/rust-bitcoin/rust-bitcoin/releases/tags/0.32.5 || true
gh api repos/lnurl-rs/lnurl-rs/releases/tags/v0.9.0 || true

# Also check if there are any related changes in the codebase that might indicate breaking changes
rg -A 3 "nostr-sdk|mostro-core|lnurl-rs|bitcoin" --type rust

Length of output: 4609

src/app/admin_add_solver.rs (1)

41-43: ⚠️ Potential issue

Add validation for trade_index value

The trade_index is unwrapped without proper validation.

-    let trade_index = inner_message.trade_index.unwrap_or(0);
+    let trade_index = match inner_message.trade_index {
+        Some(index) if index >= 0 => index,
+        Some(_) => {
+            error!("Invalid negative trade_index provided");
+            return Ok(());
+        }
+        None => 0,
+    };
src/app/rate_user.rs (1)

5-5: LGTM: Import statement updated consistently

The change from Content to Payload aligns with the codebase-wide transition in message handling.

src/app/dispute.rs (1)

14-14: Verify consistent Payload usage across the codebase

Let's verify that all message handling has been updated to use the new Payload type consistently.

✅ Verification successful

All message payloads consistently use the new Payload type

The verification shows that all message handling across the codebase has been properly updated to use the new Payload enum variants (RatingUser, Order, and Dispute). No instances of the old Content type were found, and the Payload usage is consistent across all relevant files:

  • src/app/dispute.rs: Uses Payload::Dispute
  • src/app/rate_user.rs: Uses Payload::RatingUser
  • src/app/add_invoice.rs and src/app/admin_take_dispute.rs: Use Payload::Order
  • src/flow.rs and src/util.rs: Consistently use Payload::Order
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any remaining uses of the old Content type
rg "use mostro_core::message::.*Content" 

# Check for consistent Payload usage in message handling
rg "Payload::(RatingUser|Order|Dispute)"

Length of output: 946

src/app.rs (5)

Line range hint 247-247: LGTM: Trade index assignment is correctly handled

The trade index is properly assigned based on the order type (buy/sell).

Also applies to: 250-250


Line range hint 674-680: LGTM: Proper type usage for relay URLs

The function now correctly uses RelayUrl instead of Url for better type safety.


Line range hint 753-763: LGTM: Tests updated to reflect new message structure

The test cases have been properly updated to use the new message structure and payload types.


74-75: ⚠️ Potential issue

Critical: Add error handling for JSON deserialization

The unwrap call could lead to runtime panics if the content is malformed.


209-215: ⚠️ Potential issue

Add proper error handling for message parsing

The unwrap call on JSON deserialization could cause runtime panics. Consider adding proper error handling:

-    let (message, sig): (Message, Signature) =
-        serde_json::from_str(&event.rumor.content).unwrap();
+    let (message, sig): (Message, Signature) = serde_json::from_str(&event.rumor.content)
+        .map_err(|e| {
+            tracing::error!("Failed to parse message content: {}", e);
+            return;
+        })?;

Likely invalid or redundant comment.

src/util.rs (3)

17-18: LGTM! Dependencies updated correctly.

The new imports for CantDoReason and Payload from mostro-core align with the changes in message handling.


Line range hint 615-626: LGTM! Error handling improved with CantDoReason.

The update to use CantDoReason instead of a generic message improves error handling by providing more structured and type-safe error responses.


Line range hint 674-680: LGTM! Type safety improved with RelayUrl.

The change from using generic Url to specific RelayUrl type improves type safety and makes the API more precise.

src/app/rate_user.rs Outdated Show resolved Hide resolved
src/util.rs Outdated Show resolved Hide resolved
Copy link
Collaborator

@Catrya Catrya left a comment

Choose a reason for hiding this comment

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

Hi in mostrod logs:

2024-12-13T02:26:16.251222Z  WARN mostrod::app: Error in NewOrder with context error returned from database: (code: 11) database disk image is malformed

i updated the db after do git pull

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (1)
src/util.rs (1)

167-209: Add documentation for the trade_index parameter.

The new trade_index parameter's purpose and usage should be documented for better maintainability.

+/// Publishes a new order to the network
+/// 
+/// # Arguments
+/// * `pool` - The database connection pool
+/// * `keys` - The nostr keys for signing
+/// * `new_order` - The order details to publish
+/// * `initiator_pubkey` - The public key of the order initiator
+/// * `ack_pubkey` - The public key to send acknowledgment to
+/// * `request_id` - Optional request identifier
+/// * `trade_index` - Optional trade sequence number for order tracking
 pub async fn publish_order(
     pool: &SqlitePool,
     keys: &Keys,
     new_order: &SmallOrder,
     initiator_pubkey: &str,
     ack_pubkey: PublicKey,
     request_id: Option<u64>,
     trade_index: Option<i64>,
 ) -> Result<()> {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 819670b and ca38530.

📒 Files selected for processing (2)
  • src/app/rate_user.rs (5 hunks)
  • src/util.rs (14 hunks)
🔇 Additional comments (9)
src/util.rs (4)

Line range hint 211-256: LGTM! Trade index handling is implemented correctly.

The function properly stores the trade_index for both buyer and seller scenarios.


271-273: Error handling improvement implemented as suggested.

The changes implement the previously suggested error handling for the send_event operation.


Line range hint 619-629: LGTM! Good improvement in error handling structure.

The change from using raw strings to a structured CantDoReason enum improves type safety and maintainability.


Line range hint 676-683: Verify RelayUrl type compatibility across the codebase.

The change from Url to RelayUrl improves type safety but requires verification of all usage sites.

✅ Verification successful

Let me gather more information to verify the type compatibility thoroughly.


Let me check one more aspect to ensure we have a complete picture of the type usage.


Based on the gathered information, I can now provide the final response:

The RelayUrl type change is safe and properly integrated

The change from Url to RelayUrl is verified to be safe because:

  • The get_nostr_relays() function is only used in job_relay_list() in scheduler.rs
  • The return type HashMap<RelayUrl, Relay> is properly handled in the consuming code
  • No other direct usages of the old Url type with relays were found
  • The relay data is only used for metadata tags and connection status checks
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining usage of generic Url type with relays
rg -l 'use.+url::Url' | xargs -I {} rg 'Url.*Relay' {}

# Search for all relay URL handling code
rg -l 'RelayUrl' | xargs -I {} rg 'Relay.*Url' {}

Length of output: 182


Script:

#!/bin/bash
# Search for all usages of RelayUrl type
ast-grep --pattern 'RelayUrl'

# Search for any function calls to get_nostr_relays
ast-grep --pattern 'get_nostr_relays()'

# Search for any HashMap usage with Relay types
rg 'HashMap.*Relay'

Length of output: 256


Script:

#!/bin/bash
# Check imports and type definitions related to RelayUrl
rg -A 3 'type RelayUrl'

# Check the scheduler.rs file for how it handles the relay data
cat src/scheduler.rs

# Look for any other Relay related type definitions
rg 'struct.*Relay|enum.*Relay'

Length of output: 14251

src/app/rate_user.rs (5)

5-5: LGTM: Import addition aligns with payload structure changes

The addition of Payload import aligns with the codebase-wide transition from Content to Payload type for message handling.


82-82: LGTM: Appropriate error handling for invalid order status

The error handling correctly sends a CantDo message and logs the error when the order status is invalid.


103-103: LGTM: Appropriate error handling for invalid counterpart

The error handling correctly sends a CantDo message when no valid counterpart is found.


123-129: LGTM: Rating validation implemented as suggested

The implemented rating validation matches the previous review suggestion, ensuring ratings fall within the defined range with clear error messages.


180-182: Verify the updated send_new_order_msg parameters

The changes to send_new_order_msg include new parameters for payload and an additional None value. Let's verify the function signature matches these changes.

✅ Verification successful

Based on the search results, I can now verify the function signature and its usage. The send_new_order_msg function in src/util.rs has the following signature:

pub async fn send_new_order_msg(
    request_id: Option<u64>,
    order_id: Option<Uuid>,
    action: Action,
    payload: Option<Payload>,
    destination_key: &PublicKey,
)

Looking at the code changes in the review and comparing with other usages in the codebase, particularly the RateReceived action cases, the parameters match the function signature correctly:

Some(Payload::RatingUser(rating)),  // payload parameter
&event.sender,                      // destination_key parameter
None,                              // request_id parameter

The parameter changes are correct and consistent with the function signature

The changes align with other similar usages in the codebase, particularly for rating-related actions where Payload::RatingUser is used.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for send_new_order_msg function definition
ast-grep --pattern 'fn send_new_order_msg($$$)'

Length of output: 49


Script:

#!/bin/bash
# Search for send_new_order_msg function with ripgrep for better coverage
rg "send_new_order_msg" -A 5

Length of output: 19854

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/util.rs (1)

266-266: Avoid logging sensitive information in production logs

While public keys are public, logging them may expose user identities and could be a privacy concern. Consider removing or sanitizing the log statement or adjusting the log level to debug to prevent unnecessary exposure of user information.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca38530 and 3a63750.

📒 Files selected for processing (2)
  • src/app/take_sell.rs (7 hunks)
  • src/util.rs (15 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/take_sell.rs

src/util.rs Outdated Show resolved Hide resolved
@arkanoider
Copy link
Collaborator

Hi @grunch @Catrya ,

looked back to all new management of keys in CLI and mostro to get all the new things in my mind, now i put in the above commit a small fix that should solve the issue that when a user take-sell and then ask for direct messages, now i see the message for the buyer.
We were sending back message to the main pubkey of user not to trade key.

Going on with understanding of all the flow with new key-management.

Please take a look!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (1)
src/util.rs (1)

Based on the gathered information, I can now provide the final response:

CantDoReason is underutilized in error handling

The codebase shows inconsistent usage of the CantDoReason enum. While structured reasons like InvalidTradeIndex, InvalidOrderKind, and InvalidInvoice are used in some places, most send_cant_do_msg calls pass None for the reason parameter, missing opportunities for more descriptive error handling. Key observations:

  • Only 3 instances use structured CantDoReason variants
  • 20+ calls to send_cant_do_msg pass None as the reason
  • Common error scenarios like "Not allowed", "No preimage", and "wrong status" lack corresponding CantDoReason variants

Suggested improvements:

  • Add new CantDoReason variants for common error cases (NotAllowed, NoPreimage, InvalidStatus, etc.)
  • Replace None reasons with appropriate CantDoReason variants throughout the codebase
  • Consider adding a reason parameter to Error::msg calls that lead to cant_do messages
🔗 Analysis chain

Line range hint 645-655: Verify CantDoReason handling across the codebase.

The change from generic messages to structured CantDoReason is a good improvement. Let's verify that all error cases are properly mapped to the new enum.


Let me gather more context about the CantDoReason enum definition and its variants to ensure all error cases are properly mapped.


Let me try a different approach to find the enum definition and error handling patterns.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all instances where send_cant_do_msg is called to verify CantDoReason usage

# Search for send_cant_do_msg calls
rg "send_cant_do_msg" -A 2

# Search for CantDoReason enum usage
ast-grep --pattern 'CantDoReason::$_'

Length of output: 6368


Script:

#!/bin/bash
# Find the CantDoReason enum definition
rg "enum CantDoReason" -A 10

# Find all error messages in the codebase that lead to send_cant_do_msg calls
rg "Error::msg\(.*\).*send_cant_do_msg" -B 2 -A 2

Length of output: 82


Script:

#!/bin/bash
# Search for CantDoReason in the entire codebase to find its definition
rg "CantDoReason" -B 2 -A 2

# Find error patterns near send_cant_do_msg calls
rg "send_cant_do_msg" -B 3 -A 1

Length of output: 11277

🧹 Nitpick comments (5)
src/error.rs (2)

19-19: Consider adding context to the InvalidOrderKind error variant

While the error variant is well-named, consider enhancing it to include the actual invalid order kind for better debugging and logging:

-    InvalidOrderKind,
+    InvalidOrderKind(String),

This would allow you to provide more specific error messages about which order kind was invalid and why.


41-41: Enhance error message to include more context

If you implement the suggested enhancement to InvalidOrderKind, update the Display implementation to include the context:

-            MostroError::InvalidOrderKind => write!(f, "Invalid order kind"),
+            MostroError::InvalidOrderKind(kind) => write!(f, "Invalid order kind: {}", kind),

This would provide more detailed error messages for debugging and logging purposes.

src/util.rs (3)

232-235: Remove redundant default OrderKind assignment.

The default OrderKind::Sell assignment is immediately overwritten by the match statement below. Consider removing it from the struct initialization.

 let mut new_order_db = Order {
     id: Uuid::new_v4(),
-    kind: OrderKind::Sell.to_string(),
+    kind: String::new(),
     status: Status::Pending.to_string(),

251-272: Consider using Result instead of Option for better error handling.

The function returns Option<Order> and uses early returns with None. Consider using Result<Order, MostroError> to provide more context about why the order preparation failed.

-async fn prepare_new_order(
+async fn prepare_new_order(
     new_order: &SmallOrder,
     initiator_pubkey: &str,
     trade_index: Option<i64>,
     trade_pubkey: PublicKey,
-) -> Option<Order> {
+) -> Result<Order, MostroError> {
     // ... existing code ...
     match new_order.kind {
         Some(OrderKind::Buy) => {
             new_order_db.kind = OrderKind::Buy.to_string();
             new_order_db.buyer_pubkey = Some(trade_pubkey.to_string());
             new_order_db.trade_index_buyer = trade_index;
         }
         Some(OrderKind::Sell) => {
             new_order_db.kind = OrderKind::Sell.to_string();
             new_order_db.seller_pubkey = Some(trade_pubkey.to_string());
             new_order_db.trade_index_seller = trade_index;
         }
         None => {
             send_cant_do_msg(
                 None,
                 None,
                 Some(CantDoReason::InvalidOrderKind),
                 &trade_pubkey,
             )
             .await;
-            return None;
+            return Err(MostroError::InvalidOrderKind);
         }
     }
-    Some(new_order_db)
+    Ok(new_order_db)

781-791: Add test coverage for prepare_new_order function.

The new prepare_new_order function lacks dedicated test coverage. Consider adding test cases for:

  • Valid Buy/Sell orders
  • Invalid order kind
  • Trade index handling
#[tokio::test]
async fn test_prepare_new_order() {
    let new_order = SmallOrder {
        kind: Some(OrderKind::Buy),
        // ... other fields
    };
    let trade_pubkey = Keys::generate().public_key();
    let result = prepare_new_order(&new_order, "pubkey", Some(1), trade_pubkey).await;
    assert!(result.is_some());
    // Add more assertions
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a63750 and 6f9af77.

📒 Files selected for processing (2)
  • src/error.rs (2 hunks)
  • src/util.rs (15 hunks)
🔇 Additional comments (2)
src/error.rs (1)

19-19: Verify the usage of InvalidOrderKind error

Let's ensure this error is properly integrated with the order processing logic mentioned in the PR objectives.

Also applies to: 41-41

✅ Verification successful

Let me gather more context about the usage of CantDoReason and its relationship with order processing.


Let me check for the order kind validation and error handling patterns in the codebase.


Based on the comprehensive analysis of the codebase, I can now provide a final response:

InvalidOrderKind error is properly integrated with order processing

The error is correctly used in the order validation flow:

  • It's defined in the error enum and has a proper error message implementation
  • It's used through CantDoReason::InvalidOrderKind when validating order kinds
  • The validation occurs during order processing, specifically when handling new orders and verifying their kind (buy/sell)
  • The error is properly propagated to users through the send_cant_do_msg mechanism
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how InvalidOrderKind is used in order processing code

# Look for files handling order types
echo "Files potentially handling order types:"
fd -e rs | rg -l "new-order|take-sell|take-buy"

# Check for InvalidOrderKind usage
echo -e "\nInvalidOrderKind usage:"
rg "InvalidOrderKind" -B 2 -A 2

Length of output: 949


Script:

#!/bin/bash
# Check CantDoReason enum definition and usage
echo "CantDoReason definition and usage:"
ast-grep --pattern 'enum CantDoReason { $$$ }'

echo -e "\nContext around CantDoReason::InvalidOrderKind usage:"
rg "CantDoReason::InvalidOrderKind" -B 5 -A 5

echo -e "\nFunctions handling order kinds:"
rg "fn.*order.*kind" -i

Length of output: 787


Script:

#!/bin/bash
# Look for order kind validation and related error handling
echo "Order kind validation patterns:"
rg "order.*kind" -B 3 -A 3

echo -e "\nError handling patterns with InvalidOrderKind:"
rg "InvalidOrderKind" --type rust -C 10

echo -e "\nCant-do message handling:"
rg "send_cant_do_msg" -B 5 -A 5

Length of output: 34408

src/util.rs (1)

297-299: LGTM! Error handling improvement implemented.

The error handling for send_event has been improved as suggested in the past review.

@arkanoider
Copy link
Collaborator

Some sparse questions, now in the struct below we still need master_seller_pubkey and master_buyer_pubkey, they seems not used basically and probably new key-management is doing what they were supposed to do.

Another one, in mostro.db now the field creator_pubkey is filled with the pubkey_0 of the creator, do we want to keep it in db or it would be better to remove it for privacy and have only trading keys of buyer and seller?

pub struct Order {
    pub id: Uuid,
    pub kind: String,
    pub event_id: String,
    pub hash: Option<String>,
    pub preimage: Option<String>,
    pub creator_pubkey: String,
    pub cancel_initiator_pubkey: Option<String>,
    pub buyer_pubkey: Option<String>,
    pub master_buyer_pubkey: Option<String>,
    pub seller_pubkey: Option<String>,
    pub master_seller_pubkey: Option<String>,
    pub status: String,
    pub price_from_api: bool,
    pub premium: i64,
    pub payment_method: String,
    pub amount: i64,
    pub min_amount: Option<i64>,
    pub max_amount: Option<i64>,
    pub buyer_dispute: bool,
    pub seller_dispute: bool,
    pub buyer_cooperativecancel: bool,
    pub seller_cooperativecancel: bool,
    pub fee: i64,
    pub routing_fee: i64,
    pub fiat_code: String,
    pub fiat_amount: i64,
    pub buyer_invoice: Option<String>,
    pub range_parent_id: Option<Uuid>,
    pub invoice_held_at: i64,
    pub taken_at: i64,
    pub created_at: i64,
    pub buyer_sent_rate: bool,
    pub seller_sent_rate: bool,
    pub failed_payment: bool,
    pub payment_attempts: i64,
    pub expires_at: i64,
    pub trade_index_seller: Option<i64>,
    pub trade_index_buyer: Option<i64>,
}

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.

3 participants