Skip to content

feat: add option to do search via payment id #6993

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

Open
wants to merge 2 commits into
base: development
Choose a base branch
from

Conversation

SWvheerden
Copy link
Collaborator

@SWvheerden SWvheerden commented Apr 25, 2025

Description

Motivation and Context

How Has This Been Tested?

What process can a PR reviewer use to test or verify this change?

Breaking Changes

  • None
  • Requires data directory on base node to be deleted
  • Requires hard fork
  • Other - Please specify

Summary by CodeRabbit

  • New Features

    • Added support for querying wallet balances and completed transactions filtered by a user-defined payment ID.
    • Introduced multiple formats for specifying payment IDs in balance and transaction requests (binary, string, or custom bytes).
  • Database

    • Added an optional user payment ID field to wallet tables and transaction records for enhanced filtering and tracking.
  • Configuration

    • Enabled the "grpc" feature by default in the console wallet for improved gRPC integration.

@SWvheerden SWvheerden requested a review from a team as a code owner April 25, 2025 16:00
Copy link
Contributor

coderabbitai bot commented Apr 25, 2025

Walkthrough

This update introduces support for user-defined payment identifiers across the wallet system. The gRPC API for balance and completed transactions queries now allows specifying a payment ID in multiple formats. Backend services, database schema, and migration scripts have been updated to store and filter balances and transactions by this identifier. The wallet's output manager and transaction storage layers have been extended to handle the new user_payment_id field, with corresponding changes to SQL queries and data structures. Additionally, the default features for the console wallet now include gRPC support.

Changes

File(s) / Path(s) Change Summary
applications/minotari_app_grpc/proto/wallet.proto Modified GetBalanceRequest and GetCompletedTransactionsRequest to accept a payment_id field of new type user_payment_id, which supports multiple formats (bytes, string, user bytes). Added the user_payment_id message definition.
applications/minotari_console_wallet/Cargo.toml Added "grpc" to the default feature set.
applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs Updated get_balance and get_completed_transactions methods to handle optional payment_id, extract and validate its value, and query the output manager or transaction service for filtered results. Enhanced error handling for invalid or missing payment IDs.
base_layer/wallet/migrations/2025-04-25-161400_payment_id/up.sql
base_layer/wallet/migrations/2025-04-25-161400_payment_id/down.sql
Migration: Added user_payment_id column (BLOB, nullable) to outputs, completed_transactions, inbound_transactions, and outbound_transactions tables. The down migration is a placeholder comment only.
base_layer/wallet/src/output_manager_service/handle.rs
base_layer/wallet/src/output_manager_service/service.rs
Added GetBalancePaymentId request variant and support throughout the output manager. Introduced logic to query balances filtered by payment ID.
base_layer/wallet/src/output_manager_service/storage/database/backend.rs
base_layer/wallet/src/output_manager_service/storage/database/mod.rs
Added new trait and struct methods to retrieve balances filtered by payment ID.
base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs
base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs
Implemented SQL logic for balance queries filtered by user_payment_id. Extended data structures and methods to support the new field and queries.
base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs Added user_payment_id field to NewOutputSql and initialized it in the constructor using user data from the payment ID.
base_layer/wallet/src/schema.rs Updated Diesel ORM schema to include nullable user_payment_id columns in relevant tables.
base_layer/wallet/src/transaction_service/storage/sqlite_db.rs Added user_payment_id field to SQLite representations of inbound, outbound, and completed transactions. Updated conversion logic to extract and store user payment ID bytes. Added method to find completed transactions filtered by payment ID.
base_layer/wallet/src/transaction_service/handle.rs
base_layer/wallet/src/transaction_service/service.rs
base_layer/wallet/src/transaction_service/storage/database.rs
Changed GetCompletedTransactions request variant and related methods to accept an optional payment ID filter. Updated database queries and service handlers to support filtering completed transactions by payment ID.
applications/minotari_console_wallet/src/ui/state/app_state.rs
base_layer/wallet_ffi/src/lib.rs
Updated calls to get_completed_transactions to explicitly pass None as the payment ID argument, reflecting the updated method signatures.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant WalletGrpcServer
    participant OutputManagerHandle
    participant OutputManagerService
    participant Database

    Client->>WalletGrpcServer: get_balance(request: GetBalanceRequest)
    alt payment_id provided
        WalletGrpcServer->>OutputManagerHandle: get_balance_for_payment_id(payment_id)
        OutputManagerHandle->>OutputManagerService: GetBalancePaymentId(payment_id)
        OutputManagerService->>Database: get_balance_payment_id(tip, payment_id)
        Database-->>OutputManagerService: Balance
        OutputManagerService-->>OutputManagerHandle: Balance
        OutputManagerHandle-->>WalletGrpcServer: Balance
        WalletGrpcServer-->>Client: GetBalanceResponse (filtered)
    else no payment_id
        WalletGrpcServer->>OutputManagerHandle: get_balance()
        OutputManagerHandle->>OutputManagerService: GetBalance
        OutputManagerService->>Database: get_balance(tip)
        Database-->>OutputManagerService: Balance
        OutputManagerService-->>OutputManagerHandle: Balance
        OutputManagerHandle-->>WalletGrpcServer: Balance
        WalletGrpcServer-->>Client: GetBalanceResponse (overall)
    end
Loading

Poem

🐇
A hop and a skip, a payment ID anew,
Now balances filter, just for you!
From schema to SQL, the bytes align,
With gRPC calls, responses shine.
A wallet that listens, fields that expand,
As rabbits, we code with a gentle hand!

✨ Finishing Touches
  • 📝 Generate Docstrings

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

❤️ Share
🪧 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.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @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.

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: 4

🧹 Nitpick comments (6)
base_layer/wallet/src/output_manager_service/storage/database/mod.rs (1)

187-192: The new method looks good, but needs a small formatting fix.

The implementation correctly delegates to the backend implementation, allowing balance queries filtered by payment ID.

-    pub fn get_balance_payment_id(
-        &self,
-        current_tip_for_time_lock_calculation: Option<u64>, payment_id : Vec<u8>
-    ) -> Result<Balance, OutputManagerStorageError> {
+    pub fn get_balance_payment_id(
+        &self,
+        current_tip_for_time_lock_calculation: Option<u64>,
+        payment_id: Vec<u8>
+    ) -> Result<Balance, OutputManagerStorageError> {
base_layer/wallet/migrations/2025-04-25-161400_payment_id/up.sql (1)

1-5: Remove unrelated comment from the migration file.

The comment on lines 1-2 refers to dropping and recreating a table due to removal of columns and default values, but this migration is only adding columns.

--- Any old 'outputs' will not be valid due to the removal of 'coinbase_block_height' and removal of default value for
--- 'spending_priority', so we drop and recreate the table.
ALTER TABLE outputs
    ADD user_payment_id BLOB NULL;
base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs (1)

968-988: Fix the log message to match the method name.

The implementation correctly queries balance by payment ID, but the log message still uses "get_balance" instead of "get_balance_payment_id".

    let result = OutputSql::get_balance_payment_id(current_tip_for_time_lock_calculation, payment_id, &mut conn);
    if start.elapsed().as_millis() > 0 {
        trace!(
            target: LOG_TARGET,
-            "sqlite profile - get_balance: lock {} + db_op {} = {} ms",
+            "sqlite profile - get_balance_payment_id: lock {} + db_op {} = {} ms",
            acquire_lock.as_millis(),
            (start.elapsed() - acquire_lock).as_millis(),
            start.elapsed().as_millis()
        );
    }
applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs (1)

313-327: Consider refactoring the payment ID extraction logic.

The nested match expression works but could be more readable with a more direct approach to extracting the payment ID.

- let bytes = match (user_payment_id.u256.is_empty(), user_payment_id.utf8_string.is_empty(), user_payment_id.user_bytes.is_empty()){
-     (false, true, true) => {
-         user_payment_id.u256
-     },
-     (true, false, true) => {
-         user_payment_id.utf8_string.as_bytes().to_vec()
-     },
-     (true, true, false) => {
-         user_payment_id.user_bytes
-     },
-     _ => {
-         return Err(Status::invalid_argument("user_payment_id must be one of u256, utf8_string or user_bytes".to_string()));
-     }
- };
+ // Count how many payment ID formats are provided
+ let mut provided_formats = 0;
+ let mut bytes = Vec::new();
+ 
+ if !user_payment_id.u256.is_empty() {
+     provided_formats += 1;
+     bytes = user_payment_id.u256;
+ }
+ 
+ if !user_payment_id.utf8_string.is_empty() {
+     provided_formats += 1;
+     bytes = user_payment_id.utf8_string.as_bytes().to_vec();
+ }
+ 
+ if !user_payment_id.user_bytes.is_empty() {
+     provided_formats += 1;
+     bytes = user_payment_id.user_bytes;
+ }
+ 
+ if provided_formats != 1 {
+     return Err(Status::invalid_argument("user_payment_id must be provided in exactly one format: u256, utf8_string, or user_bytes".to_string()));
+ }
base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs (2)

530-534: Fix spacing in method signature.

There's a missing space after the colon in the parameter declaration.

- pub fn get_balance_payment_id(
-     current_tip_for_time_lock_calculation: Option<u64>,
-     payment_id:Vec<u8>,
-     conn: &mut SqliteConnection,
- ) -> Result<Balance, OutputManagerStorageError> {
+ pub fn get_balance_payment_id(
+     current_tip_for_time_lock_calculation: Option<u64>,
+     payment_id: Vec<u8>,
+     conn: &mut SqliteConnection,
+ ) -> Result<Balance, OutputManagerStorageError> {

528-645: Consider refactoring to reduce code duplication with get_balance method.

The get_balance_payment_id method is nearly identical to the existing get_balance method, with the main difference being the additional filtering by user_payment_id. This duplication could lead to maintenance issues if one method is updated but the other is not.

Consider refactoring both methods to use a common helper function that accepts an optional payment ID parameter, reducing duplication and ensuring consistent handling.

// Add a new private helper method
+ fn get_balance_internal(
+     current_tip_for_time_lock_calculation: Option<u64>,
+     payment_id: Option<Vec<u8>>,
+     conn: &mut SqliteConnection,
+ ) -> Result<Balance, OutputManagerStorageError> {
+     // Implementation that handles both cases
+ }
+
+ pub fn get_balance(
+     current_tip_for_time_lock_calculation: Option<u64>,
+     conn: &mut SqliteConnection,
+ ) -> Result<Balance, OutputManagerStorageError> {
+     Self::get_balance_internal(current_tip_for_time_lock_calculation, None, conn)
+ }
+
+ pub fn get_balance_payment_id(
+     current_tip_for_time_lock_calculation: Option<u64>,
+     payment_id: Vec<u8>,
+     conn: &mut SqliteConnection,
+ ) -> Result<Balance, OutputManagerStorageError> {
+     Self::get_balance_internal(current_tip_for_time_lock_calculation, Some(payment_id), conn)
+ }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 693cd7a and 205601f.

📒 Files selected for processing (14)
  • applications/minotari_app_grpc/proto/wallet.proto (1 hunks)
  • applications/minotari_console_wallet/Cargo.toml (1 hunks)
  • applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs (1 hunks)
  • base_layer/wallet/migrations/2025-04-25-161400_payment_id/down.sql (1 hunks)
  • base_layer/wallet/migrations/2025-04-25-161400_payment_id/up.sql (1 hunks)
  • base_layer/wallet/src/output_manager_service/handle.rs (3 hunks)
  • base_layer/wallet/src/output_manager_service/service.rs (2 hunks)
  • base_layer/wallet/src/output_manager_service/storage/database/backend.rs (1 hunks)
  • base_layer/wallet/src/output_manager_service/storage/database/mod.rs (1 hunks)
  • base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs (1 hunks)
  • base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs (3 hunks)
  • base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs (2 hunks)
  • base_layer/wallet/src/schema.rs (4 hunks)
  • base_layer/wallet/src/transaction_service/storage/sqlite_db.rs (9 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
base_layer/wallet/src/output_manager_service/storage/database/mod.rs (4)
base_layer/wallet/src/output_manager_service/storage/database/backend.rs (1)
  • get_balance_payment_id (91-91)
base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs (1)
  • get_balance_payment_id (968-988)
base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs (1)
  • get_balance_payment_id (530-645)
base_layer/wallet/src/output_manager_service/service.rs (1)
  • get_balance_payment_id (816-820)
base_layer/wallet/src/output_manager_service/service.rs (4)
base_layer/wallet/src/output_manager_service/storage/database/backend.rs (1)
  • get_balance_payment_id (91-91)
base_layer/wallet/src/output_manager_service/storage/database/mod.rs (1)
  • get_balance_payment_id (187-192)
base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs (1)
  • get_balance_payment_id (968-988)
base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs (1)
  • get_balance_payment_id (530-645)
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: test (mainnet, stagenet)
  • GitHub Check: test (nextnet, nextnet)
  • GitHub Check: test (testnet, esmeralda)
  • GitHub Check: cargo check with stable
  • GitHub Check: Cucumber tests / FFI
  • GitHub Check: Cucumber tests / Base Layer
🔇 Additional comments (22)
applications/minotari_console_wallet/Cargo.toml (1)

83-83: LGTM: gRPC feature enabled by default.

Adding "grpc" to the default feature set is appropriate for supporting the new payment ID search functionality.

base_layer/wallet/src/output_manager_service/storage/database/backend.rs (1)

90-91: LGTM: Well-designed method for payment ID filtering.

The new method extends the balance retrieval functionality in a clean way by maintaining a consistent interface with the existing get_balance method while adding payment ID filtering capability.

applications/minotari_app_grpc/proto/wallet.proto (1)

258-260: LGTM: GetBalanceRequest updated to support payment ID filtering.

The GetBalanceRequest message now includes a payment_id field, enabling clients to request balances filtered by a specific identifier.

base_layer/wallet/src/output_manager_service/service.rs (2)

337-344: Handler for new GetBalancePaymentId request looks good.

The implementation correctly follows the same pattern as the existing GetBalance handler, retrieving chain metadata from the base node service and passing the payment_id parameter to the new get_balance_payment_id method.


816-820: Implementation of get_balance_payment_id method is well structured.

The method correctly retrieves the balance from the database using the payment_id filter, logs it at the trace level, and returns the result. The implementation mirrors the existing get_balance method, maintaining consistency in the codebase.

base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs (3)

69-69: New user_payment_id field added to NewOutputSql struct.

The field is correctly defined as Option<Vec> to match the schema definition, allowing for nullable user payment IDs.


82-88: Proper extraction of user_payment_id from payment_id.

The implementation correctly extracts the user data from the payment_id and handles empty data appropriately by setting to None when empty, which is storage-efficient.


124-124: Struct initialization includes the new user_payment_id field.

The field is consistently included in the struct initialization, completing the implementation of the new field.

base_layer/wallet/src/schema.rs (1)

40-40: Schema updated consistently across all relevant tables.

The nullable binary column user_payment_id has been added to all necessary tables (completed_transactions, inbound_transactions, outbound_transactions, and outputs) with consistent type definitions, enabling proper filtering of balances by payment ID.

Also applies to: 56-56, 83-83, 124-124

base_layer/wallet/src/output_manager_service/handle.rs (3)

64-64: Feature implementation looks good.

This new variant in the OutputManagerRequest enum adds the capability to filter balance queries by payment ID, which is exactly what the PR aims to achieve.


169-169: Implementation of Display trait is appropriate.

The formatting for the new enum variant provides sufficient information without exposing potentially sensitive payment ID data.


502-507: New method is well implemented.

The get_balance_for_payment_id method follows the same pattern as other methods in this struct, with proper error handling and async/await flow. Good implementation that integrates seamlessly with the existing codebase.

base_layer/wallet/src/transaction_service/storage/sqlite_db.rs (8)

1155-1155: New field added correctly.

Adding user_payment_id as an optional field to the InboundTransactionSql struct properly supports the new feature.


1331-1336: Good handling of empty payment IDs.

The code correctly extracts user data from payment IDs and handles empty values by converting them to None, which will be stored as NULL in the database. This improves storage efficiency.


1348-1348: Field initialized properly.

The extracted user_payment_id value is correctly assigned to the struct field.


1588-1593: Consistent implementation for OutboundTransactionSql.

The same user payment ID extraction logic is applied consistently here, matching the implementation in InboundTransactionSql.


1606-1606: Field assigned correctly.

The user_payment_id value is properly included in the struct initialization.


1701-1701: New field added to CompletedTransactionSql.

The user_payment_id field is consistently added to this struct, maintaining parity with the other transaction SQL structs.


1968-1973: Consistent extraction logic applied.

The same user payment ID extraction logic is applied here, ensuring consistent behavior across all transaction types.


1994-1994: Field initialized properly in constructor.

The user_payment_id is correctly assigned in the struct initialization.

applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs (1)

310-338: Implement payment ID filtering in balance retrieval.

The implementation correctly adds support for retrieving balances filtered by payment ID, handling multiple input formats appropriately. The error handling for invalid arguments is clear and informative.

A few observations about the implementation:

  1. It correctly validates that only one of the three payment ID formats is provided
  2. It properly converts between different representations (bytes, strings)
  3. It returns appropriate error status codes for different failure scenarios
  4. The method gracefully falls back to the original behavior when no payment ID is provided
base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs (1)

113-113: Add user_payment_id field to OutputSql struct.

The addition of an optional field to store user-defined payment IDs is appropriate. Using Option<Vec<u8>> allows for flexibility in the payment ID format and ensures compatibility with various consumer implementations.

@@ -0,0 +1 @@
-- This file should undo anything in `up.sql`
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

Incomplete migration rollback script.

The down.sql file contains only a placeholder comment without the necessary SQL statements to undo the schema changes made in the corresponding up.sql migration.

Based on the AI summary, you need to add statements to drop the user_payment_id columns:

-- This file should undo anything in `up.sql`
+ALTER TABLE outputs DROP COLUMN user_payment_id;
+ALTER TABLE completed_transactions DROP COLUMN user_payment_id;
+ALTER TABLE inbound_transactions DROP COLUMN user_payment_id;
+ALTER TABLE outbound_transactions DROP COLUMN user_payment_id;
📝 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
-- This file should undo anything in `up.sql`
-- This file should undo anything in `up.sql`
ALTER TABLE outputs DROP COLUMN user_payment_id;
ALTER TABLE completed_transactions DROP COLUMN user_payment_id;
ALTER TABLE inbound_transactions DROP COLUMN user_payment_id;
ALTER TABLE outbound_transactions DROP COLUMN user_payment_id;

Comment on lines +262 to +266
message user_payment_id {
bytes u256 = 1;
string utf8_string = 2;
bytes user_bytes = 3;
}
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

Improve the user_payment_id message using oneof and follow naming conventions.

The current implementation allows clients to potentially provide multiple payment ID formats simultaneously, which could lead to ambiguity. Also, the message name doesn't follow protobuf naming conventions.

Apply these improvements:

-message user_payment_id {
-  bytes u256 = 1;
-  string utf8_string = 2;
-  bytes user_bytes = 3;
+message UserPaymentId {
+  oneof id_format {
+    bytes u256 = 1;
+    string utf8_string = 2;
+    bytes user_bytes = 3;
+  }
}

Also update the reference:

-    user_payment_id payment_id = 1;
+    UserPaymentId payment_id = 1;

Using oneof ensures exactly one format is provided, eliminating potential ambiguity in client requests.

Comment on lines +580 to +588
"SELECT coalesce(sum(value), 0) as amount, 'available_balance' as category \
FROM outputs WHERE status = ? AND user_payment_id = ?\
UNION ALL \
SELECT coalesce(sum(value), 0) as amount, 'pending_incoming_balance' as category \
FROM outputs WHERE source != ? AND status = ? OR status = ? OR status = ? AND user_payment_id = ? \
UNION ALL \
SELECT coalesce(sum(value), 0) as amount, 'pending_outgoing_balance' as category \
FROM outputs WHERE status = ? OR status = ? OR status = ? AND user_payment_id = ?",
)
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

Fix SQL query WHERE clause grouping for non-time-locked balances.

Similar to the previous issue, the SQL query without time lock calculations also has incorrect placement of the AND user_payment_id = ? condition.

 SELECT coalesce(sum(value), 0) as amount, 'pending_incoming_balance' as category \
-FROM outputs WHERE source != ? AND status = ? OR status = ? OR status = ? AND user_payment_id = ? \
+FROM outputs WHERE source != ? AND (status = ? OR status = ? OR status = ?) AND user_payment_id = ? \
 UNION ALL \
 SELECT coalesce(sum(value), 0) as amount, 'pending_outgoing_balance' as category \
-FROM outputs WHERE status = ? OR status = ? OR status = ? AND user_payment_id = ?",
+FROM outputs WHERE (status = ? OR status = ? OR status = ?) AND user_payment_id = ?",
📝 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
"SELECT coalesce(sum(value), 0) as amount, 'available_balance' as category \
FROM outputs WHERE status = ? AND user_payment_id = ?\
UNION ALL \
SELECT coalesce(sum(value), 0) as amount, 'pending_incoming_balance' as category \
FROM outputs WHERE source != ? AND status = ? OR status = ? OR status = ? AND user_payment_id = ? \
UNION ALL \
SELECT coalesce(sum(value), 0) as amount, 'pending_outgoing_balance' as category \
FROM outputs WHERE status = ? OR status = ? OR status = ? AND user_payment_id = ?",
)
"SELECT coalesce(sum(value), 0) as amount, 'available_balance' as category \
FROM outputs WHERE status = ? AND user_payment_id = ?\
UNION ALL \
SELECT coalesce(sum(value), 0) as amount, 'pending_incoming_balance' as category \
FROM outputs WHERE source != ? AND (status = ? OR status = ? OR status = ?) AND user_payment_id = ? \
UNION ALL \
SELECT coalesce(sum(value), 0) as amount, 'pending_outgoing_balance' as category \
FROM outputs WHERE (status = ? OR status = ? OR status = ?) AND user_payment_id = ?",

Comment on lines +544 to +554
"SELECT coalesce(sum(value), 0) as amount, 'available_balance' as category \
FROM outputs WHERE status = ? AND maturity <= ? AND script_lock_height <= ? AND user_payment_id = ? \
UNION ALL \
SELECT coalesce(sum(value), 0) as amount, 'time_locked_balance' as category \
FROM outputs WHERE status = ? AND maturity > ? OR script_lock_height > ? AND user_payment_id = ? \
UNION ALL \
SELECT coalesce(sum(value), 0) as amount, 'pending_incoming_balance' as category \
FROM outputs WHERE source != ? AND status = ? OR status = ? OR status = ? AND user_payment_id = ? \
UNION ALL \
SELECT coalesce(sum(value), 0) as amount, 'pending_outgoing_balance' as category \
FROM outputs WHERE status = ? OR status = ? OR status = ? AND user_payment_id = ?",
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

Fix SQL query WHERE clause grouping for time-locked balances.

The SQL query has incorrect placement of the AND user_payment_id = ? condition in relation to the OR conditions, which could lead to unexpected results when filtering outputs.

The condition AND user_payment_id = ? at the end of each query section will only apply to the last condition in the OR chain, not to the entire group of conditions. This could result in retrieving balances that don't match the payment ID.

 SELECT coalesce(sum(value), 0) as amount, 'time_locked_balance' as category \
-FROM outputs WHERE status = ? AND maturity > ? OR script_lock_height > ? AND user_payment_id = ? \
+FROM outputs WHERE status = ? AND ((maturity > ? OR script_lock_height > ?) AND user_payment_id = ?) \
 UNION ALL \
 SELECT coalesce(sum(value), 0) as amount, 'pending_incoming_balance' as category \
-FROM outputs WHERE source != ? AND status = ? OR status = ? OR status = ? AND user_payment_id = ? \
+FROM outputs WHERE source != ? AND (status = ? OR status = ? OR status = ?) AND user_payment_id = ? \
 UNION ALL \
 SELECT coalesce(sum(value), 0) as amount, 'pending_outgoing_balance' as category \
-FROM outputs WHERE status = ? OR status = ? OR status = ? AND user_payment_id = ?",
+FROM outputs WHERE (status = ? OR status = ? OR status = ?) AND user_payment_id = ?",
📝 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
"SELECT coalesce(sum(value), 0) as amount, 'available_balance' as category \
FROM outputs WHERE status = ? AND maturity <= ? AND script_lock_height <= ? AND user_payment_id = ? \
UNION ALL \
SELECT coalesce(sum(value), 0) as amount, 'time_locked_balance' as category \
FROM outputs WHERE status = ? AND maturity > ? OR script_lock_height > ? AND user_payment_id = ? \
UNION ALL \
SELECT coalesce(sum(value), 0) as amount, 'pending_incoming_balance' as category \
FROM outputs WHERE source != ? AND status = ? OR status = ? OR status = ? AND user_payment_id = ? \
UNION ALL \
SELECT coalesce(sum(value), 0) as amount, 'pending_outgoing_balance' as category \
FROM outputs WHERE status = ? OR status = ? OR status = ? AND user_payment_id = ?",
"SELECT coalesce(sum(value), 0) as amount, 'available_balance' as category \
FROM outputs WHERE status = ? AND maturity <= ? AND script_lock_height <= ? AND user_payment_id = ? \
UNION ALL \
SELECT coalesce(sum(value), 0) as amount, 'time_locked_balance' as category \
FROM outputs WHERE status = ? AND ((maturity > ? OR script_lock_height > ?) AND user_payment_id = ?) \
UNION ALL \
SELECT coalesce(sum(value), 0) as amount, 'pending_incoming_balance' as category \
FROM outputs WHERE source != ? AND (status = ? OR status = ? OR status = ?) AND user_payment_id = ? \
UNION ALL \
SELECT coalesce(sum(value), 0) as amount, 'pending_outgoing_balance' as category \
FROM outputs WHERE (status = ? OR status = ? OR status = ?) AND user_payment_id = ?",

Copy link

github-actions bot commented Apr 25, 2025

Test Results (CI)

0 tests   0 ✅  0s ⏱️
0 suites  0 💤
0 files    0 ❌

Results for commit c8829ba.

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Apr 25, 2025

Test Results (Integration tests)

0 tests   0 ✅  0s ⏱️
0 suites  0 💤
0 files    0 ❌

Results for commit c8829ba.

♻️ This comment has been updated with latest results.

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 (4)
base_layer/wallet_ffi/src/lib.rs (1)

8211-8638: Consider extending FFI layer to expose payment_id filtering functionality.

All calls to get_completed_transactions have been updated with the None parameter to indicate no payment ID filtering. However, the FFI layer doesn't seem to expose the new payment ID filtering functionality to callers.

If this is intentional (perhaps planned for a future update), that's fine. Otherwise, consider updating the FFI function signatures to accept an optional payment ID parameter and pass it through to the transaction service.

base_layer/wallet/src/transaction_service/handle.rs (1)

82-970: Consider documenting the payment ID parameter

While the implementation is correct, it would be helpful to add documentation comments explaining the payment ID parameter's purpose and expected format for better developer experience.

-    GetCompletedTransactions(Option<Vec<u8>>),
+    /// GetCompletedTransactions retrieves all completed transactions
+    /// optionally filtered by a user payment ID
+    GetCompletedTransactions(Option<Vec<u8>>),

And for the method:

-    pub async fn get_completed_transactions(
-        &mut self,
-        payment_id: Option<Vec<u8>>,
-    ) -> Result<Vec<CompletedTransaction>, TransactionServiceError> {
+    /// Retrieves completed transactions from the transaction service
+    /// 
+    /// # Parameters
+    /// * `payment_id` - Optional byte vector representing a payment ID to filter transactions
+    ///
+    /// # Returns
+    /// A vector of completed transactions, optionally filtered by payment ID
+    pub async fn get_completed_transactions(
+        &mut self,
+        payment_id: Option<Vec<u8>>,
+    ) -> Result<Vec<CompletedTransaction>, TransactionServiceError> {
base_layer/wallet/src/transaction_service/storage/database.rs (2)

629-656: Consider parameter order consistency

The method signature changed from:

fn get_completed_transactions_by_cancelled(&self, cancelled: bool)

to:

fn get_completed_transactions_by_cancelled(&self, payment_id: Option<Vec<u8>>, cancelled: bool)

It's generally better to append new optional parameters rather than inserting them before existing ones to maintain a more intuitive API evolution.

Consider refactoring to:

-fn get_completed_transactions_by_cancelled(
-    &self,
-    payment_id: Option<Vec<u8>>,
-    cancelled: bool,
-) -> Result<Vec<CompletedTransaction>, TransactionStorageError> {
+fn get_completed_transactions_by_cancelled(
+    &self,
+    cancelled: bool,
+    payment_id: Option<Vec<u8>>,
+) -> Result<Vec<CompletedTransaction>, TransactionStorageError> {

And update all callers accordingly.


159-162: Add logging for payment ID filtering

For better observability and debugging, consider adding logging when filtering by payment ID.

pub fn get_completed_transactions_by_payment_id(
    &self,
    payment_id: Vec<u8>,
) -> Result<Vec<CompletedTransaction>, TransactionStorageError> {
+    debug!(
+        target: LOG_TARGET,
+        "Retrieving completed transactions filtered by payment ID"
+    );
    let t = self.db.find_completed_transactions_for_payment_id(payment_id)?;
    Ok(t)
}

Also applies to: 485-491

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 205601f and c8829ba.

📒 Files selected for processing (15)
  • applications/minotari_app_grpc/proto/wallet.proto (1 hunks)
  • applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs (2 hunks)
  • applications/minotari_console_wallet/src/ui/state/app_state.rs (1 hunks)
  • base_layer/wallet/src/output_manager_service/handle.rs (3 hunks)
  • base_layer/wallet/src/output_manager_service/service.rs (2 hunks)
  • base_layer/wallet/src/output_manager_service/storage/database/backend.rs (1 hunks)
  • base_layer/wallet/src/output_manager_service/storage/database/mod.rs (1 hunks)
  • base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs (1 hunks)
  • base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs (3 hunks)
  • base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs (2 hunks)
  • base_layer/wallet/src/transaction_service/handle.rs (3 hunks)
  • base_layer/wallet/src/transaction_service/service.rs (1 hunks)
  • base_layer/wallet/src/transaction_service/storage/database.rs (4 hunks)
  • base_layer/wallet/src/transaction_service/storage/sqlite_db.rs (10 hunks)
  • base_layer/wallet_ffi/src/lib.rs (6 hunks)
✅ Files skipped from review due to trivial changes (1)
  • applications/minotari_console_wallet/src/ui/state/app_state.rs
🚧 Files skipped from review as they are similar to previous changes (10)
  • base_layer/wallet/src/output_manager_service/storage/database/mod.rs
  • base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs
  • base_layer/wallet/src/output_manager_service/handle.rs
  • base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs
  • applications/minotari_app_grpc/proto/wallet.proto
  • base_layer/wallet/src/output_manager_service/service.rs
  • base_layer/wallet/src/output_manager_service/storage/database/backend.rs
  • base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs
  • base_layer/wallet/src/transaction_service/storage/sqlite_db.rs
  • applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: test (testnet, esmeralda)
  • GitHub Check: test (mainnet, stagenet)
  • GitHub Check: test (nextnet, nextnet)
  • GitHub Check: ci
  • GitHub Check: cargo check with stable
  • GitHub Check: Cucumber tests / FFI
  • GitHub Check: Cucumber tests / Base Layer
🔇 Additional comments (14)
base_layer/wallet/src/transaction_service/service.rs (1)

893-895: Implementation of payment ID filtering for completed transactions looks good.

The code correctly modifies the GetCompletedTransactions request handler to accept and pass a payment_id parameter to the database query, enabling filtering of completed transactions by payment identifier as intended.

base_layer/wallet_ffi/src/lib.rs (6)

8211-8211: Updated API call to include payment_id parameter.

This change adapts the call to the updated get_completed_transactions method signature which now accepts an optional payment ID filter parameter. The None value indicates that no payment ID filtering is applied in this context.


8279-8279: Updated API call to include payment_id parameter.

This change adapts the call to the updated get_completed_transactions method signature which now accepts an optional payment ID filter parameter. The None value indicates that no payment ID filtering is applied in this context.


8349-8349: Updated API call to include payment_id parameter.

This change adapts the call to the updated get_completed_transactions method signature which now accepts an optional payment ID filter parameter. The None value indicates that no payment ID filtering is applied in this context.


8504-8504: Updated API call to include payment_id parameter.

This change adapts the call to the updated get_completed_transactions method signature which now accepts an optional payment ID filter parameter. The None value indicates that no payment ID filtering is applied in this context.


8565-8565: Updated API call to include payment_id parameter.

This change adapts the call to the updated get_completed_transactions method signature which now accepts an optional payment ID filter parameter. The None value indicates that no payment ID filtering is applied in this context.


8638-8638: Updated API call to include payment_id parameter.

This change adapts the call to the updated get_completed_transactions method signature which now accepts an optional payment ID filter parameter. The None value indicates that no payment ID filtering is applied in this context.

base_layer/wallet/src/transaction_service/handle.rs (3)

82-82: Added payment ID filter to GetCompletedTransactions

The enum variant has been updated to include an optional payment ID parameter (Option<Vec<u8>>), enabling transaction filtering by user-defined payment identifiers.


211-211: LGTM for Display implementation update

The Display trait implementation was appropriately updated to handle the new parameter, maintaining a clean string representation without exposing the filter details.


963-970: LGTM for method signature and implementation update

The get_completed_transactions method signature and implementation were updated to accept an optional payment ID parameter and pass it through to the service request correctly.

base_layer/wallet/src/transaction_service/storage/database.rs (4)

159-162: LGTM for new TransactionBackend trait method

The new method find_completed_transactions_for_payment_id extends the backend trait with payment ID filtering capability.


485-491: LGTM for additional helper method

The new get_completed_transactions_by_payment_id method provides a clean API to retrieve transactions filtered by payment ID.


599-604: LGTM for updated method signatures

The get_completed_transactions and get_cancelled_completed_transactions methods were correctly updated to support the payment ID filter parameter.

Also applies to: 607-607


638-655:

✅ Verification successful

Verify transaction backend implementations

The changes assume that each backend implementation of TransactionBackend will properly handle the new find_completed_transactions_for_payment_id method. Ensure all implementations are updated, including test doubles if any.


🏁 Script executed:

#!/bin/bash

# Check all backend implementations for the new method
echo "Searching for all TransactionBackend implementors..."
rg -A 5 "impl\s+.*TransactionBackend\s+for\s+.*" --type rust

# Check if they implement the new method
echo "Checking for find_completed_transactions_for_payment_id implementations..."
rg "fn\s+find_completed_transactions_for_payment_id" --type rust

Length of output: 1349


All TransactionBackend implementations updated

I’ve verified that there are only two impl TransactionBackend for … in the repo (the SQLite backend and the in-code default), and both now define find_completed_transactions_for_payment_id. No additional test-double implementations of TransactionBackend were found. There are no outstanding updates required here.

Comment on lines 629 to 656
fn get_completed_transactions_by_cancelled(
&self,
payment_id: Option<Vec<u8>>,
cancelled: bool,
) -> Result<Vec<CompletedTransaction>, TransactionStorageError> {
let key = if cancelled {
DbKey::CancelledCompletedTransactions
} else {
DbKey::CompletedTransactions
};

let t = match self.db.fetch(&key) {
Ok(None) => log_error(
key,
TransactionStorageError::UnexpectedResult("Could not retrieve completed transactions".to_string()),
),
Ok(Some(DbValue::CompletedTransactions(pt))) => Ok(pt),
Ok(Some(other)) => unexpected_result(key, other),
Err(e) => log_error(key, e),
}?;
Ok(t)
match payment_id {
Some(payment_id) => self.db.find_completed_transactions_for_payment_id(payment_id),
None => {
let t = match self.db.fetch(&key) {
Ok(None) => log_error(
key,
TransactionStorageError::UnexpectedResult(
"Could not retrieve completed transactions".to_string(),
),
),
Ok(Some(DbValue::CompletedTransactions(pt))) => Ok(pt),
Ok(Some(other)) => unexpected_result(key, other),
Err(e) => log_error(key, e),
}?;
Ok(t)
},
}
}
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

Potential issue with cancelled status filtering

When a payment ID is provided, the method directly calls find_completed_transactions_for_payment_id without considering the cancelled parameter. This could lead to unexpected results where cancelled transactions are included when they shouldn't be, depending on the backend implementation.

Ensure the cancelled filter is applied when using a payment ID by modifying the implementation:

fn get_completed_transactions_by_cancelled(
    &self,
    payment_id: Option<Vec<u8>>,
    cancelled: bool,
) -> Result<Vec<CompletedTransaction>, TransactionStorageError> {
    let key = if cancelled {
        DbKey::CancelledCompletedTransactions
    } else {
        DbKey::CompletedTransactions
    };
    match payment_id {
-        Some(payment_id) => self.db.find_completed_transactions_for_payment_id(payment_id),
+        Some(payment_id) => {
+            let transactions = self.db.find_completed_transactions_for_payment_id(payment_id)?;
+            // Filter by cancelled status to maintain consistent behavior
+            let filtered = transactions
+                .into_iter()
+                .filter(|tx| (tx.cancelled.is_some()) == cancelled)
+                .collect();
+            Ok(filtered)
+        },
        None => {
            let t = match self.db.fetch(&key) {
                Ok(None) => log_error(
                    key,
                    TransactionStorageError::UnexpectedResult(
                        "Could not retrieve completed transactions".to_string(),
                    ),
                ),
                Ok(Some(DbValue::CompletedTransactions(pt))) => Ok(pt),
                Ok(Some(other)) => unexpected_result(key, other),
                Err(e) => log_error(key, e),
            }?;
            Ok(t)
        },
    }
}

Alternatively, you could extend the trait method to accept the cancelled status:

-fn find_completed_transactions_for_payment_id(
-    &self,
-    payment_id: Vec<u8>,
-) -> Result<Vec<CompletedTransaction>, TransactionStorageError>;
+fn find_completed_transactions_for_payment_id(
+    &self,
+    payment_id: Vec<u8>,
+    cancelled: bool,
+) -> Result<Vec<CompletedTransaction>, TransactionStorageError>;

And update all implementations accordingly.

📝 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
fn get_completed_transactions_by_cancelled(
&self,
payment_id: Option<Vec<u8>>,
cancelled: bool,
) -> Result<Vec<CompletedTransaction>, TransactionStorageError> {
let key = if cancelled {
DbKey::CancelledCompletedTransactions
} else {
DbKey::CompletedTransactions
};
let t = match self.db.fetch(&key) {
Ok(None) => log_error(
key,
TransactionStorageError::UnexpectedResult("Could not retrieve completed transactions".to_string()),
),
Ok(Some(DbValue::CompletedTransactions(pt))) => Ok(pt),
Ok(Some(other)) => unexpected_result(key, other),
Err(e) => log_error(key, e),
}?;
Ok(t)
match payment_id {
Some(payment_id) => self.db.find_completed_transactions_for_payment_id(payment_id),
None => {
let t = match self.db.fetch(&key) {
Ok(None) => log_error(
key,
TransactionStorageError::UnexpectedResult(
"Could not retrieve completed transactions".to_string(),
),
),
Ok(Some(DbValue::CompletedTransactions(pt))) => Ok(pt),
Ok(Some(other)) => unexpected_result(key, other),
Err(e) => log_error(key, e),
}?;
Ok(t)
},
}
}
fn get_completed_transactions_by_cancelled(
&self,
payment_id: Option<Vec<u8>>,
cancelled: bool,
) -> Result<Vec<CompletedTransaction>, TransactionStorageError> {
let key = if cancelled {
DbKey::CancelledCompletedTransactions
} else {
DbKey::CompletedTransactions
};
match payment_id {
Some(payment_id) => {
let transactions = self
.db
.find_completed_transactions_for_payment_id(payment_id)?;
// Filter by cancelled status to maintain consistent behavior
let filtered = transactions
.into_iter()
.filter(|tx| (tx.cancelled.is_some()) == cancelled)
.collect();
Ok(filtered)
},
None => {
let t = match self.db.fetch(&key) {
Ok(None) => log_error(
key,
TransactionStorageError::UnexpectedResult(
"Could not retrieve completed transactions".to_string(),
),
),
Ok(Some(DbValue::CompletedTransactions(pt))) => Ok(pt),
Ok(Some(other)) => unexpected_result(key, other),
Err(e) => log_error(key, e),
}?;
Ok(t)
},
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant