Skip to content

feat: validate payment id size#7335

Merged
SWvheerden merged 48 commits intotari-project:developmentfrom
MCozhusheck:feat/validate_payment_id_size
Jul 22, 2025
Merged

feat: validate payment id size#7335
SWvheerden merged 48 commits intotari-project:developmentfrom
MCozhusheck:feat/validate_payment_id_size

Conversation

@MCozhusheck
Copy link
Copy Markdown
Contributor

@MCozhusheck MCozhusheck commented Jul 16, 2025

Description

Closes: #7096 and #7052

Renames PaymentId to MemoField and adds constructor methods with input validation, as the total size of a MemoField is limited to 256 bytes.

Breaking Changes

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

The PaymentId type has been renamed to MemoField. All external applications using this type will need to update their code to reflect the new naming.

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced strict validation and a 256-byte size limit for payment IDs, with clear error messages for invalid entries.
    • Added new methods for creating and inspecting payment IDs, improving flexibility and user feedback.
    • Added new error variants to improve reporting of invalid payment ID formats in transaction and output management services.
  • Bug Fixes

    • Improved error handling and user feedback when entering invalid or oversized payment IDs in transaction forms.
  • Refactor

    • Standardized payment ID creation and usage throughout the application for consistency and maintainability.
    • Simplified extraction of transaction and address details from payment IDs using new accessor methods.
    • Improved code clarity by replacing explicit pattern matching with method calls on payment IDs.
    • Reformatted code for better readability and removed unused imports.
    • Renamed payment ID fields and methods to "memo field" for clearer terminology.
    • Updated transaction, wallet, and output management modules to use the new memo field abstraction.
  • Tests

    • Enhanced and expanded unit tests to cover new validation logic and edge cases for payment IDs.
    • Updated test cases to use new payment ID constructors and error handling methods.

@MCozhusheck MCozhusheck reopened this Jul 17, 2025
@github-actions
Copy link
Copy Markdown

Test Results (Integration tests)

1 tests   1 ✅  0s ⏱️
1 suites  0 💤
2 files    0 ❌
1 errors

For more details on these parsing errors, see this check.

Results for commit fc6f4c5.

@MCozhusheck MCozhusheck marked this pull request as ready for review July 17, 2025 14:44
@MCozhusheck MCozhusheck requested a review from a team as a code owner July 17, 2025 14:44
@MCozhusheck
Copy link
Copy Markdown
Contributor Author

Marking it as ready for review just to get some code rabbit comments, do not merge yet

Copy link
Copy Markdown
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: 9

🔭 Outside diff range comments (10)
base_layer/wallet/src/output_manager_service/service.rs (7)

2052-2055: Consider error handling for PaymentId construction.

The function call PaymentId::open_from_string() returns a PaymentId directly, but based on the pattern of other similar calls in the file, this should also use a constructor method and handle potential errors.

-            .with_payment_id(PaymentId::open_from_string(
-                "Pay to self transaction",
-                TxType::PaymentToSelf,
-            ));
+            .with_payment_id(PaymentId::open_from_string(
+                "Pay to self transaction",
+                TxType::PaymentToSelf,
+            )
+            .map_err(|e| OutputManagerError::ServiceError(format!("Invalid payment ID: {}", e)))?);

2502-2508: Consider error handling for PaymentId construction.

The .unwrap() call could panic if the PaymentId construction fails. Consider handling this error more gracefully.

-            .with_payment_id(PaymentId::open_from_string(
-                &format!(
-                    "Coin split transaction, {} into {} outputs",
-                    accumulated_amount, number_of_splits
-                ),
-                TxType::CoinSplit,
-            ))
+            .with_payment_id(PaymentId::open_from_string(
+                &format!(
+                    "Coin split transaction, {} into {} outputs",
+                    accumulated_amount, number_of_splits
+                ),
+                TxType::CoinSplit,
+            )
+            .map_err(|e| OutputManagerError::BuildError(format!("Invalid payment ID: {}", e)))?)

2536-2536: Consider error handling for PaymentId construction.

The function call could fail during PaymentId construction. Consider handling this error more gracefully.

-                    PaymentId::open_from_string(&format!("{} even coin splits", number_of_splits), TxType::CoinSplit),
+                    PaymentId::open_from_string(&format!("{} even coin splits", number_of_splits), TxType::CoinSplit)
+                        .map_err(|e| OutputManagerError::ServiceError(format!("Invalid payment ID: {}", e)))?,

2673-2676: Consider error handling for PaymentId construction.

The function call could fail during PaymentId construction. Consider handling this error more gracefully.

-        let payment_id = PaymentId::open_from_string(
-            &format!("Coin split, {} into {} outputs", accumulated_amount, number_of_splits),
-            TxType::CoinSplit,
-        );
+        let payment_id = PaymentId::open_from_string(
+            &format!("Coin split, {} into {} outputs", accumulated_amount, number_of_splits),
+            TxType::CoinSplit,
+        )
+        .map_err(|e| OutputManagerError::ServiceError(format!("Invalid payment ID: {}", e)))?;

3034-3034: Consider error handling for PaymentId construction.

The function call could fail during PaymentId construction. Consider handling this error more gracefully.

-            .with_payment_id(PaymentId::open_from_string("scraping wallet", TxType::PaymentToOther))
+            .with_payment_id(PaymentId::open_from_string("scraping wallet", TxType::PaymentToOther)
+                .map_err(|e| OutputManagerError::ServiceError(format!("Invalid payment ID: {}", e)))?)

3131-3134: Consider error handling for PaymentId construction.

The function call could fail during PaymentId construction. Consider handling this error more gracefully.

-                    .with_payment_id(PaymentId::open_from_string(
-                        "SHA-XTR atomic swap",
-                        TxType::ClaimAtomicSwap,
-                    ))
+                    .with_payment_id(PaymentId::open_from_string(
+                        "SHA-XTR atomic swap",
+                        TxType::ClaimAtomicSwap,
+                    )
+                    .map_err(|e| OutputManagerError::ServiceError(format!("Invalid payment ID: {}", e)))?)

3214-3217: Consider error handling for PaymentId construction.

The function call could fail during PaymentId construction. Consider handling this error more gracefully.

-            .with_payment_id(PaymentId::open_from_string(
-                "SHA-XTR atomic refund",
-                TxType::HtlcAtomicSwapRefund,
-            ))
+            .with_payment_id(PaymentId::open_from_string(
+                "SHA-XTR atomic refund",
+                TxType::HtlcAtomicSwapRefund,
+            )
+            .map_err(|e| OutputManagerError::ServiceError(format!("Invalid payment ID: {}", e)))?)
base_layer/core/src/transactions/transaction_components/payment_id.rs (3)

497-525: Replace panic! with proper error handling

The add_sender_address method uses panic! when new_address_and_data fails, which could cause runtime crashes if the total size exceeds 256 bytes. This should return a Result instead.

pub fn add_sender_address(
    self,
    sender_address: TariAddress,
    sender_one_sided: bool,
    fee: MicroMinotari,
    tx_type: Option<TxType>,
-) -> PaymentId {
+) -> Result<PaymentId, String> {
    match self.inner {
        InnerPaymentId::Open { user_data, tx_type } => {
-            match PaymentId::new_address_and_data(sender_address, fee, sender_one_sided, tx_type, user_data) {
-                Ok(payment_id) => payment_id,
-                Err(e) => panic!("Cannot create AddressAndData PaymentId: {}", e),
-            }
+            PaymentId::new_address_and_data(sender_address, fee, sender_one_sided, tx_type, user_data)
        },
        InnerPaymentId::Empty => {
-            match PaymentId::new_address_and_data(
+            PaymentId::new_address_and_data(
                sender_address,
                fee,
                sender_one_sided,
                tx_type.unwrap_or_default(),
                vec![],
-            ) {
-                Ok(payment_id) => payment_id,
-                Err(e) => panic!("Cannot create AddressAndData PaymentId: {}", e),
-            }
+            )
        },
-        _ => self,
+        _ => Ok(self),
    }
}

609-636: Validate lengths before converting to u8

The expect() calls could panic if address length, user data length, or hash count exceed 255. Add validation to return an error or handle gracefully.

Consider validating these lengths before the conversion:

if address_bytes.len() > 255 || user_data.len() > 255 {
    // Handle error - perhaps store in Raw variant or return error
}

For sent_output_hashes on line 635:

if sent_output_hashes.len() > 255 {
    // Handle error appropriately
}

657-669: Document the premine edge case

This special handling for values < 1000 appears to be a compatibility hack. Please add a comment explaining why this edge case exists and what premine scenario it addresses.

// edge case for premine:
+// Historical compatibility: Early premine transactions used 8-byte values < 1000
+// that need to be parsed as Open variant with PaymentToOther type
if bytes.len() == SIZE_VALUE {
♻️ Duplicate comments (2)
base_layer/core/src/transactions/transaction_protocol/sender.rs (1)

1384-1385: Same change as above.

Also applies to: 1510-1511, 1623-1624, 1816-1817

applications/minotari_node/src/grpc/base_node_grpc_server.rs (1)

1446-1448: Same unchecked constructor used here — see previous comment for rationale and suggested fix.

🧹 Nitpick comments (5)
base_layer/core/src/transactions/transaction_components/wallet_output_builder.rs (2)

380-382: Minor repetition in tests

PaymentId::new_empty() is invoked several times in this file’s tests.
If a Default impl exists (or could be added) returning the empty variant, test code could simply call PaymentId::default() to reduce repetition. Pure nit – feel free to ignore.

Also applies to: 422-424


123-137: Optional micro-optimization: avoid one clone

self.payment_id = payment_id.clone(); is followed by passing payment_id by value to encrypt_data_for_recovery, forcing an extra allocation.
A tiny saving can be made by:

self.payment_id = payment_id.clone(); // keep
self.encrypted_data = key_manager
    .encrypt_data_for_recovery(
        &self.commitment_mask_key_id,
        custom_recovery_key_id,
        self.value.as_u64(),
-       payment_id,
+       self.payment_id.clone(), // reuse existing clone
    )
    .await?;

Not critical, but reads slightly cleaner.

base_layer/wallet/tests/transaction_service_tests/service.rs (1)

622-623: PaymentId::new_empty() looks good – consider Default for terseness

The direct replacement of the removed PaymentId::Empty enum with the validated constructor PaymentId::new_empty() aligns with the new API and keeps the tests compiling.
If the PaymentId type now implements Default, you could shave a few characters off and improve readability by passing PaymentId::default() instead, which conveys the same “empty” intent without introducing another semantic alias.

This is purely a nicety; the current code is perfectly correct.

Also applies to: 1485-1486, 1789-1790, 1911-1912, 2018-2019, 2061-2062, 2559-2560, 2689-2691

applications/minotari_console_wallet/src/ui/state/app_state.rs (1)

1051-1056: Condense burn-flag calculation for readability

The two-level if/else if ... else can be collapsed into a single boolean expression, which reads more clearly and eliminates three explicit true/false literals.

-let burn = if tx.transaction.body.contains_burn() {
-    true
-} else if let Some(tx_type) = tx.payment_id.get_tx_type() {
-    tx_type == TxType::Burn
-} else {
-    false
-};
+let burn = tx.transaction.body.contains_burn()
+    || matches!(tx.payment_id.get_tx_type(), Some(TxType::Burn));

No behavioral change – just simpler, idiomatic Rust without branching.

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

3895-3899: Logic condition can be simplified for readability

The current check is correct but hard to parse:

if !(completed_tx.status == TransactionStatus::Completed
     || completed_tx.status == TransactionStatus::Broadcast
     || completed_tx.status == TransactionStatus::MinedUnconfirmed)
     || completed_tx.transaction.body.kernels().is_empty()

A clearer version:

+let is_allowed_status = matches!(
+    completed_tx.status,
+    TransactionStatus::Completed
+        | TransactionStatus::Broadcast
+        | TransactionStatus::MinedUnconfirmed
+);
+
+if !is_allowed_status || completed_tx.transaction.body.kernels().is_empty() {

Same semantics, greatly improved readability.

Comment on lines 1048 to 1118
/// Unchecked constructor for Empty payment ID
/// Use this for migration from old direct enum construction
pub fn empty() -> Self {
PaymentId {
inner: InnerPaymentId::Empty,
}
}

/// Unchecked constructor for Open payment ID
/// Use this for migration from old direct enum construction
/// WARNING: This bypasses validation - use new_open() for new code
pub fn open_unchecked(user_data: Vec<u8>, tx_type: TxType) -> Self {
PaymentId {
inner: InnerPaymentId::Open { user_data, tx_type },
}
}

/// Unchecked constructor for Raw payment ID
/// Use this for migration from old direct enum construction
/// WARNING: This bypasses validation - use new_raw() for new code
pub fn raw_unchecked(data: Vec<u8>) -> Self {
PaymentId {
inner: InnerPaymentId::Raw(data),
}
}

/// Unchecked constructor for AddressAndData payment ID
/// Use this for migration from old direct enum construction
/// WARNING: This bypasses validation - use new_address_and_data() for new code
pub fn address_and_data_unchecked(
sender_address: TariAddress,
sender_one_sided: bool,
fee: MicroMinotari,
tx_type: TxType,
user_data: Vec<u8>,
) -> Self {
PaymentId {
inner: InnerPaymentId::AddressAndData {
sender_address,
sender_one_sided,
fee,
tx_type,
user_data,
},
}
}

/// Unchecked constructor for TransactionInfo payment ID
/// Use this for migration from old direct enum construction
/// WARNING: This bypasses validation - use new_transaction_info() for new code
pub fn transaction_info_unchecked(
recipient_address: TariAddress,
sender_one_sided: bool,
amount: MicroMinotari,
fee: MicroMinotari,
tx_type: TxType,
sent_output_hashes: Vec<FixedHash>,
user_data: Vec<u8>,
) -> Self {
PaymentId {
inner: InnerPaymentId::TransactionInfo {
recipient_address,
sender_one_sided,
amount,
fee,
tx_type,
sent_output_hashes,
user_data,
},
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add deprecation attributes to unchecked constructors

These unchecked constructors bypass validation and should be marked with #[deprecated] to discourage usage and ensure they're removed in future versions.

/// Unchecked constructor for Empty payment ID
/// Use this for migration from old direct enum construction
+#[deprecated(since = "0.1.0", note = "Use new_empty() instead")]
pub fn empty() -> Self {

/// Unchecked constructor for Open payment ID
/// Use this for migration from old direct enum construction
/// WARNING: This bypasses validation - use new_open() for new code
+#[deprecated(since = "0.1.0", note = "Use new_open() instead - this bypasses validation")]
pub fn open_unchecked(user_data: Vec<u8>, tx_type: TxType) -> Self {

Apply similar deprecation attributes to the other unchecked constructors.

🤖 Prompt for AI Agents
In base_layer/core/src/transactions/transaction_components/payment_id.rs between
lines 1048 and 1118, the unchecked constructors bypass validation and should be
marked with #[deprecated] attributes to discourage their use and signal planned
removal. Add #[deprecated] annotations with appropriate messages to all these
unchecked constructor functions, including empty(), open_unchecked(),
raw_unchecked(), address_and_data_unchecked(), and transaction_info_unchecked(),
to clearly indicate they bypass validation and recommend using the corresponding
new_ constructors instead.

Copy link
Copy Markdown
Collaborator

@SWvheerden SWvheerden left a comment

Choose a reason for hiding this comment

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

This looking good and in the right direction.
I do think while we are changing everything here, lets add this: #7052

Make sure we handle the errors properly and don't panic

};

// Maximum size for a PaymentID in bytes (256 bytes)
const MAX_PAYMENT_ID_SIZE: usize = 256;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this is a consensus constant

InnerPaymentId::Open { user_data, tx_type } => {
match PaymentId::new_address_and_data(sender_address, fee, sender_one_sided, tx_type, user_data) {
Ok(payment_id) => payment_id,
Err(e) => panic!("Cannot create AddressAndData PaymentId: {}", e),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

never panic, should create an error here, and propagate that back out.


#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Default)]
pub enum PaymentId {
pub struct PaymentId {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
pub struct PaymentId {
pub struct MemoField {

}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Default)]
pub enum InnerPaymentId {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
pub enum InnerPaymentId {
enum InnerMemoField {

Dont expose this at all

Copy link
Copy Markdown
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

♻️ Duplicate comments (1)
base_layer/core/src/transactions/transaction_components/payment_id.rs (1)

1063-1125: Add deprecation attributes to unchecked constructors

These unchecked constructors bypass validation and should be marked with #[deprecated] attributes as previously suggested.

🧹 Nitpick comments (2)
base_layer/core/src/transactions/transaction_components/payment_id.rs (2)

38-40: Fix typo in comment

The comment has a grammatical error.

-// We pad the bytes to min this size, so that we can use the same size for AddressAndData and TransactionInfo
+// We pad the bytes to minimum this size, so that we can use the same size for AddressAndData and TransactionInfo

1127-1157: Consider documenting the default address behavior

The get_transaction_info method returns TariAddress::default() for missing sender/recipient addresses with a comment "Will be set by caller based on context". This implicit behavior could be confusing.

Consider either:

  1. Returning Option<TariAddress> for addresses that need to be set by the caller
  2. Adding clearer documentation about this behavior in the method's doc comment
  3. Using a more explicit placeholder or error state instead of the default address
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ec5b89f and 86db463.

📒 Files selected for processing (9)
  • base_layer/core/src/transactions/transaction_components/encrypted_data.rs (4 hunks)
  • base_layer/core/src/transactions/transaction_components/payment_id.rs (29 hunks)
  • base_layer/wallet/src/output_manager_service/error.rs (2 hunks)
  • base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs (2 hunks)
  • base_layer/wallet/src/output_manager_service/service.rs (22 hunks)
  • base_layer/wallet/src/transaction_service/offline_signing/offline_signer.rs (4 hunks)
  • base_layer/wallet/src/transaction_service/service.rs (21 hunks)
  • base_layer/wallet/src/utxo_scanner_service/utxo_scanner_task.rs (2 hunks)
  • base_layer/wallet_ffi/src/lib.rs (15 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs
  • base_layer/wallet/src/transaction_service/offline_signing/offline_signer.rs
  • base_layer/core/src/transactions/transaction_components/encrypted_data.rs
  • base_layer/wallet/src/utxo_scanner_service/utxo_scanner_task.rs
  • base_layer/wallet/src/transaction_service/service.rs
  • base_layer/wallet_ffi/src/lib.rs
  • base_layer/wallet/src/output_manager_service/service.rs
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ksrichard
PR: tari-project/tari#7129
File: applications/minotari_node/src/http/handler/sync_utxos_by_block.rs:21-29
Timestamp: 2025-05-29T20:59:32.020Z
Learning: In the Tari codebase, request validation for RPC services is handled at the service layer using the `serde_valid` crate rather than at the HTTP handler level. For example, `SyncUtxosByBlockRequest` validates that limit is between 1-5 and page is minimum 0. HTTP handlers should delegate validation to the service layer rather than implementing their own validation.
base_layer/wallet/src/output_manager_service/error.rs (2)
Learnt from: hansieodendaal
PR: tari-project/tari#6963
File: common_sqlite/src/error.rs:88-92
Timestamp: 2025-05-23T07:49:57.349Z
Learning: In the StorageError enum in common_sqlite/src/error.rs, the HexError variant should keep the manual From<HexError> implementation rather than using #[from] attribute, as it stores a String representation of the error rather than the HexError type itself.
Learnt from: hansieodendaal
PR: tari-project/tari#7284
File: applications/minotari_console_wallet/src/automation/commands.rs:0-0
Timestamp: 2025-07-15T12:23:14.650Z
Learning: In applications/minotari_console_wallet/src/automation/commands.rs, the consistent error handling pattern for command execution is to use match statements that: 1) On success: log with debug!, print user feedback, and push tx_id to tx_ids vector for monitoring, 2) On error: print error message with eprintln! using the format "{CommandName} error! {}", rather than using .unwrap() which would panic.
base_layer/core/src/transactions/transaction_components/payment_id.rs (7)
Learnt from: hansieodendaal
PR: tari-project/tari#6963
File: common_sqlite/src/error.rs:88-92
Timestamp: 2025-05-23T07:49:57.349Z
Learning: In the StorageError enum in common_sqlite/src/error.rs, the HexError variant should keep the manual From<HexError> implementation rather than using #[from] attribute, as it stores a String representation of the error rather than the HexError type itself.
Learnt from: hansieodendaal
PR: tari-project/tari#7189
File: integration_tests/tests/steps/wallet_cli_steps.rs:464-467
Timestamp: 2025-06-07T06:49:32.174Z
Learning: In integration test code, using panic! for setup validation and prerequisite checking is acceptable and often preferred, as it provides immediate feedback when test scenarios are incorrectly configured rather than allowing tests to continue in invalid states.
Learnt from: hansieodendaal
PR: tari-project/tari#7284
File: applications/minotari_console_wallet/src/automation/commands.rs:0-0
Timestamp: 2025-07-15T12:23:14.650Z
Learning: In applications/minotari_console_wallet/src/automation/commands.rs, the consistent error handling pattern for command execution is to use match statements that: 1) On success: log with debug!, print user feedback, and push tx_id to tx_ids vector for monitoring, 2) On error: print error message with eprintln! using the format "{CommandName} error! {}", rather than using .unwrap() which would panic.
Learnt from: SWvheerden
PR: tari-project/tari#7111
File: integration_tests/build.rs:7-12
Timestamp: 2025-06-02T07:28:49.228Z
Learning: In build scripts (build.rs files), using `.unwrap()` on environment variables is acceptable and preferred when you want the build to fail if the required environment variables are not set, rather than using fallbacks.
Learnt from: hansieodendaal
PR: tari-project/tari#7216
File: base_layer/core/src/base_node/comms_interface/comms_request.rs:146-148
Timestamp: 2025-06-14T04:56:43.247Z
Learning: In the Tari codebase, `HashOutput` is a type alias for `FixedHash`, and `FixedHash` already implements `Display` trait with hex formatting by calling `self.0.to_hex()` internally. This means when formatting `HashOutput` values, there's no need to explicitly call `.to_hex()` as the Display implementation handles hex conversion automatically.
Learnt from: hansieodendaal
PR: tari-project/tari#7280
File: base_layer/core/src/chain_storage/blockchain_database.rs:361-438
Timestamp: 2025-07-04T10:56:46.079Z
Learning: The combination of `tokio::task::spawn(async move {` with `tokio::task::spawn_blocking().await` in the payref rebuild background task works well and shuts down properly with the tokio environment, as confirmed by testing in the Tari codebase.
Learnt from: ksrichard
PR: tari-project/tari#7129
File: applications/minotari_node/src/http/handler/sync_utxos_by_block.rs:21-29
Timestamp: 2025-05-29T20:59:32.020Z
Learning: In the Tari codebase, request validation for RPC services is handled at the service layer using the `serde_valid` crate rather than at the HTTP handler level. For example, `SyncUtxosByBlockRequest` validates that limit is between 1-5 and page is minimum 0. HTTP handlers should delegate validation to the service layer rather than implementing their own validation.
🔇 Additional comments (4)
base_layer/core/src/transactions/transaction_components/payment_id.rs (3)

186-344: Well-designed validation implementation

The constructor methods correctly implement size validation with detailed error messages and proper Result types, avoiding panics as requested in previous reviews. The recursive size calculation for nested PaymentIds in addresses is particularly well thought out.


483-503: Proper error propagation in add_sender_address

Good change to make this method return a Result, properly propagating validation errors from the new constructors.


1996-2331: Excellent test coverage for validation logic

The test suite comprehensively covers the new validation logic including:

  • Size limits for all PaymentId variants
  • Recursive PaymentId validation in addresses
  • Padding behavior
  • Edge cases and maximum valid sizes
  • Serialization round-trip testing

Well done on the thorough testing approach.

base_layer/wallet/src/output_manager_service/error.rs (1)

157-158: Appropriate error variant for PaymentId validation

The addition of InvalidPaymentIdFormat(String) error variant properly supports the new PaymentId validation logic, allowing detailed error messages to be propagated from the validation methods.

Copy link
Copy Markdown
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: 3

🧹 Nitpick comments (5)
base_layer/core/src/transactions/transaction_key_manager/interface.rs (1)

385-385: LGTM: Return type correctly updated to MemoField.

The return type change is consistent with the refactor. Consider updating the method name in a future refactor to use "memo_field" terminology for consistency, though keeping "payment_id" may be intentional for API compatibility.

base_layer/core/src/transactions/transaction_components/memo_field.rs (4)

44-45: Document the purpose of padding constants

The constants PADDING_SIZE and PADDING_SIZE_NO_TAG would benefit from documentation explaining their purpose and why these specific values (130 and 129) were chosen.

-// We pad the bytes to min this size, so that we can use the same size for AddressAndData and TransactionInfo
+// We pad the bytes to min this size, so that we can use the same size for AddressAndData and TransactionInfo
+// This ensures consistent serialization size and helps prevent size-based information leakage
 const PADDING_SIZE: usize = 130;
+// Size without the tag byte (used for validation during deserialization)
 const PADDING_SIZE_NO_TAG: usize = 129;

219-292: Consider using a proper error type instead of String

The constructor methods return Result<Self, String> for validation errors. Consider defining a proper error enum for better error handling and type safety.

+#[derive(Debug, Clone, PartialEq)]
+pub enum MemoFieldError {
+    SizeExceeded { 
+        total_size: usize, 
+        limit: usize,
+        details: String 
+    },
+}
+
+impl std::fmt::Display for MemoFieldError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            MemoFieldError::SizeExceeded { total_size, limit, details } => {
+                write!(f, "PaymentId exceeds {}-byte limit: {} bytes ({})", limit, total_size, details)
+            }
+        }
+    }
+}
+
+impl std::error::Error for MemoFieldError {}

 pub fn new_address_and_data(
     sender_address: TariAddress,
     fee: MicroMinotari,
     sender_one_sided: bool,
     tx_type: TxType,
     payment_id: Vec<u8>,
-) -> Result<Self, String> {
+) -> Result<Self, MemoFieldError> {
     // Calculate the actual size this PaymentId would occupy (including any nested PaymentIds in the address)
     let total_size = Self::calculate_address_and_data_size(&sender_address, payment_id.len());
 
     if total_size > MAX_ENCRYPTED_DATA_SIZE {
-        return Err(format!(
-            "PaymentId exceeds {}-byte limit: {} bytes (address: {} bytes, payment_id: {} bytes, overhead: {} \
-             bytes)",
-            MAX_ENCRYPTED_DATA_SIZE,
-            total_size,
-            sender_address.get_size(),
-            payment_id.len(),
-            total_size - sender_address.get_size() - payment_id.len()
-        ));
+        return Err(MemoFieldError::SizeExceeded {
+            total_size,
+            limit: MAX_ENCRYPTED_DATA_SIZE,
+            details: format!(
+                "address: {} bytes, payment_id: {} bytes, overhead: {} bytes",
+                sender_address.get_size(),
+                payment_id.len(),
+                total_size - sender_address.get_size() - payment_id.len()
+            ),
+        });
     }

639-651: Document the premine edge case handling

The special handling for values < 1000 in 8-byte payloads appears to be a workaround for historical premine transactions. This should be documented more clearly.

 let raw_bytes = bytes.to_vec();
-// edge case for premine:
+// Special case for historical premine transactions:
+// Early premine outputs used 8-byte values that should be interpreted as Open payment IDs
+// rather than raw values when the value is less than 1000
 if bytes.len() == SIZE_VALUE {
     let bytes_array: [u8; SIZE_VALUE] = bytes.try_into().expect("We already test the length");
     let v = u64::from_le_bytes(bytes_array);
     if v < 1000 {
         return MemoField {
             inner: InnerMemoField::Open {
                 tx_type: TxType::PaymentToOther,
                 payment_id: bytes.to_vec(),
             },
         };
     }
 }

1249-1404: Consider extracting test data generation to fixtures

The create_test_data_array function is quite long. Consider extracting common test addresses and payment IDs to test fixtures or constants for better reusability across tests.

+mod test_fixtures {
+    use super::*;
+    
+    pub const TEST_SINGLE_ADDRESS: &str = "f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk";
+    pub const TEST_DUAL_ADDRESS: &str = "f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb";
+    
+    pub fn single_address() -> TariAddress {
+        TariAddress::from_base58(TEST_SINGLE_ADDRESS).unwrap()
+    }
+    
+    pub fn dual_address() -> TariAddress {
+        TariAddress::from_base58(TEST_DUAL_ADDRESS).unwrap()
+    }
+}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 86db463 and 04dadab.

📒 Files selected for processing (64)
  • applications/minotari_console_wallet/src/automation/commands.rs (27 hunks)
  • applications/minotari_console_wallet/src/grpc/mod.rs (3 hunks)
  • applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs (23 hunks)
  • applications/minotari_console_wallet/src/notifier/mod.rs (2 hunks)
  • applications/minotari_console_wallet/src/ui/components/burn_tab.rs (2 hunks)
  • applications/minotari_console_wallet/src/ui/components/send_tab.rs (2 hunks)
  • applications/minotari_console_wallet/src/ui/components/transactions_tab.rs (7 hunks)
  • applications/minotari_console_wallet/src/ui/state/app_state.rs (5 hunks)
  • applications/minotari_console_wallet/src/ui/state/tasks.rs (3 hunks)
  • applications/minotari_merge_mining_proxy/src/block_template_manager.rs (2 hunks)
  • applications/minotari_miner/src/run_miner.rs (2 hunks)
  • applications/minotari_node/src/grpc/base_node_grpc_server.rs (3 hunks)
  • base_layer/common_types/src/tari_address/dual_address.rs (10 hunks)
  • base_layer/common_types/src/tari_address/mod.rs (2 hunks)
  • base_layer/contacts/src/contacts_service/storage/sqlite_db.rs (1 hunks)
  • base_layer/core/src/blocks/pre_mine/mod.rs (2 hunks)
  • base_layer/core/src/test_helpers/mod.rs (2 hunks)
  • base_layer/core/src/transactions/coinbase_builder.rs (23 hunks)
  • base_layer/core/src/transactions/test_helpers.rs (5 hunks)
  • base_layer/core/src/transactions/transaction_components/encrypted_data.rs (5 hunks)
  • base_layer/core/src/transactions/transaction_components/memo_field.rs (1 hunks)
  • base_layer/core/src/transactions/transaction_components/mod.rs (1 hunks)
  • base_layer/core/src/transactions/transaction_components/test.rs (2 hunks)
  • base_layer/core/src/transactions/transaction_components/unblinded_output.rs (2 hunks)
  • base_layer/core/src/transactions/transaction_components/wallet_output.rs (5 hunks)
  • base_layer/core/src/transactions/transaction_components/wallet_output_builder.rs (6 hunks)
  • base_layer/core/src/transactions/transaction_key_manager/inner.rs (4 hunks)
  • base_layer/core/src/transactions/transaction_key_manager/interface.rs (2 hunks)
  • base_layer/core/src/transactions/transaction_key_manager/wrapper.rs (4 hunks)
  • base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs (2 hunks)
  • base_layer/core/src/transactions/transaction_protocol/recipient.rs (2 hunks)
  • base_layer/core/src/transactions/transaction_protocol/sender.rs (9 hunks)
  • base_layer/core/src/transactions/transaction_protocol/single_receiver.rs (5 hunks)
  • base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs (4 hunks)
  • base_layer/core/src/validation/block_body/test.rs (2 hunks)
  • base_layer/core/tests/tests/node_comms_interface.rs (3 hunks)
  • base_layer/tari_mining_helper_ffi/src/lib.rs (2 hunks)
  • base_layer/wallet/src/output_manager_service/error.rs (1 hunks)
  • base_layer/wallet/src/output_manager_service/handle.rs (10 hunks)
  • base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs (3 hunks)
  • base_layer/wallet/src/output_manager_service/service.rs (21 hunks)
  • base_layer/wallet/src/output_manager_service/storage/models.rs (2 hunks)
  • base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs (1 hunks)
  • base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs (2 hunks)
  • base_layer/wallet/src/transaction_service/handle.rs (23 hunks)
  • base_layer/wallet/src/transaction_service/offline_signing/models.rs (2 hunks)
  • base_layer/wallet/src/transaction_service/offline_signing/offline_signer.rs (3 hunks)
  • base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs (3 hunks)
  • base_layer/wallet/src/transaction_service/service.rs (22 hunks)
  • base_layer/wallet/src/transaction_service/storage/database.rs (2 hunks)
  • base_layer/wallet/src/transaction_service/storage/models.rs (11 hunks)
  • base_layer/wallet/src/transaction_service/storage/sqlite_db.rs (24 hunks)
  • base_layer/wallet/src/wallet.rs (7 hunks)
  • base_layer/wallet/tests/output_manager_service_tests/service.rs (16 hunks)
  • base_layer/wallet/tests/support/utils.rs (3 hunks)
  • base_layer/wallet/tests/transaction_service_tests/service.rs (44 hunks)
  • base_layer/wallet/tests/transaction_service_tests/storage.rs (12 hunks)
  • base_layer/wallet_ffi/src/callback_handler_tests.rs (6 hunks)
  • base_layer/wallet_ffi/src/lib.rs (20 hunks)
  • integration_tests/src/ffi/wallet.rs (3 hunks)
  • integration_tests/src/miner.rs (2 hunks)
  • integration_tests/src/wallet_ffi.rs (2 hunks)
  • integration_tests/tests/steps/wallet_ffi_steps.rs (3 hunks)
  • integration_tests/tests/steps/wallet_steps.rs (17 hunks)
✅ Files skipped from review due to trivial changes (11)
  • base_layer/wallet/src/output_manager_service/error.rs
  • base_layer/wallet/src/transaction_service/storage/database.rs
  • base_layer/core/src/transactions/transaction_components/mod.rs
  • base_layer/wallet/src/transaction_service/offline_signing/models.rs
  • base_layer/core/src/validation/block_body/test.rs
  • applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs
  • base_layer/wallet/src/output_manager_service/handle.rs
  • base_layer/wallet/src/transaction_service/storage/models.rs
  • base_layer/core/src/transactions/transaction_components/wallet_output.rs
  • base_layer/common_types/src/tari_address/dual_address.rs
  • base_layer/wallet/src/transaction_service/storage/sqlite_db.rs
🚧 Files skipped from review as they are similar to previous changes (31)
  • base_layer/core/src/transactions/transaction_protocol/recipient.rs
  • base_layer/wallet/tests/support/utils.rs
  • applications/minotari_merge_mining_proxy/src/block_template_manager.rs
  • base_layer/core/src/blocks/pre_mine/mod.rs
  • base_layer/core/src/transactions/transaction_protocol/single_receiver.rs
  • base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs
  • base_layer/wallet/tests/transaction_service_tests/storage.rs
  • base_layer/core/src/transactions/transaction_components/test.rs
  • base_layer/core/tests/tests/node_comms_interface.rs
  • applications/minotari_console_wallet/src/ui/components/send_tab.rs
  • applications/minotari_miner/src/run_miner.rs
  • base_layer/core/src/test_helpers/mod.rs
  • base_layer/wallet_ffi/src/lib.rs
  • base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs
  • applications/minotari_console_wallet/src/ui/components/burn_tab.rs
  • base_layer/tari_mining_helper_ffi/src/lib.rs
  • applications/minotari_node/src/grpc/base_node_grpc_server.rs
  • applications/minotari_console_wallet/src/ui/components/transactions_tab.rs
  • integration_tests/src/miner.rs
  • base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs
  • base_layer/wallet/src/wallet.rs
  • base_layer/core/src/transactions/transaction_components/encrypted_data.rs
  • base_layer/core/src/transactions/transaction_components/wallet_output_builder.rs
  • base_layer/wallet/tests/output_manager_service_tests/service.rs
  • base_layer/wallet/tests/transaction_service_tests/service.rs
  • base_layer/core/src/transactions/transaction_protocol/sender.rs
  • base_layer/wallet/src/transaction_service/offline_signing/offline_signer.rs
  • base_layer/core/src/transactions/test_helpers.rs
  • applications/minotari_console_wallet/src/ui/state/app_state.rs
  • base_layer/core/src/transactions/coinbase_builder.rs
  • base_layer/wallet/src/transaction_service/service.rs
🧰 Additional context used
🧠 Learnings (18)
📓 Common learnings
Learnt from: ksrichard
PR: tari-project/tari#7129
File: applications/minotari_node/src/http/handler/sync_utxos_by_block.rs:21-29
Timestamp: 2025-05-29T20:59:32.020Z
Learning: In the Tari codebase, request validation for RPC services is handled at the service layer using the `serde_valid` crate rather than at the HTTP handler level. For example, `SyncUtxosByBlockRequest` validates that limit is between 1-5 and page is minimum 0. HTTP handlers should delegate validation to the service layer rather than implementing their own validation.
applications/minotari_console_wallet/src/notifier/mod.rs (1)
Learnt from: hansieodendaal
PR: tari-project/tari#7284
File: applications/minotari_console_wallet/src/automation/commands.rs:0-0
Timestamp: 2025-07-15T12:23:14.650Z
Learning: In applications/minotari_console_wallet/src/automation/commands.rs, the consistent error handling pattern for command execution is to use match statements that: 1) On success: log with debug!, print user feedback, and push tx_id to tx_ids vector for monitoring, 2) On error: print error message with eprintln! using the format "{CommandName} error! {}", rather than using .unwrap() which would panic.
base_layer/wallet/src/output_manager_service/storage/models.rs (2)
Learnt from: hansieodendaal
PR: tari-project/tari#7266
File: base_layer/core/src/chain_storage/blockchain_database.rs:457-461
Timestamp: 2025-06-26T13:18:55.898Z
Learning: The MinedInfo struct in the Tari blockchain database contains two optional fields: `input: Option<InputMinedInfo>` and `output: Option<OutputMinedInfo>`. This design allows methods returning MinedInfo to always return a valid struct rather than wrapping it in Option, with "not found" scenarios represented through the internal optional fields.
Learnt from: martinserts
PR: tari-project/tari#7122
File: base_layer/wallet/src/transaction_service/offline_signing/one_sided_signer.rs:0-0
Timestamp: 2025-06-16T14:49:31.831Z
Learning: In Tari's one-sided transaction offline signing implementation, the script_keys vector in the script offset calculation should only include input.output.script_key_id, not output script keys, change output script keys, or recipient script keys. This is by design for the one-sided transaction protocol.
integration_tests/tests/steps/wallet_ffi_steps.rs (1)
Learnt from: hansieodendaal
PR: tari-project/tari#7284
File: applications/minotari_console_wallet/src/automation/commands.rs:0-0
Timestamp: 2025-07-15T12:23:14.650Z
Learning: In applications/minotari_console_wallet/src/automation/commands.rs, the consistent error handling pattern for command execution is to use match statements that: 1) On success: log with debug!, print user feedback, and push tx_id to tx_ids vector for monitoring, 2) On error: print error message with eprintln! using the format "{CommandName} error! {}", rather than using .unwrap() which would panic.
applications/minotari_console_wallet/src/grpc/mod.rs (2)
Learnt from: hansieodendaal
PR: tari-project/tari#7284
File: applications/minotari_console_wallet/src/automation/commands.rs:0-0
Timestamp: 2025-07-15T12:23:14.650Z
Learning: In applications/minotari_console_wallet/src/automation/commands.rs, the consistent error handling pattern for command execution is to use match statements that: 1) On success: log with debug!, print user feedback, and push tx_id to tx_ids vector for monitoring, 2) On error: print error message with eprintln! using the format "{CommandName} error! {}", rather than using .unwrap() which would panic.
Learnt from: SolfataraEmit
PR: tari-project/tari#6994
File: docs/src/API_GRPC_Explanation.md:236-244
Timestamp: 2025-04-28T11:41:15.722Z
Learning: The minotari_console_wallet reports a fourth field `timelocked_balance` in the terminal output for the `getBalance` method, which should be reflected in the documentation for accuracy. The `wallet.proto` file confirms that `timelocked_balance` is defined as the fourth field in the `GetBalanceResponse` message.
base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs (1)
Learnt from: hansieodendaal
PR: tari-project/tari#6963
File: common_sqlite/src/error.rs:88-92
Timestamp: 2025-05-23T07:49:57.349Z
Learning: In the StorageError enum in common_sqlite/src/error.rs, the HexError variant should keep the manual From<HexError> implementation rather than using #[from] attribute, as it stores a String representation of the error rather than the HexError type itself.
base_layer/contacts/src/contacts_service/storage/sqlite_db.rs (2)
Learnt from: hansieodendaal
PR: tari-project/tari#6963
File: common_sqlite/src/error.rs:88-92
Timestamp: 2025-05-23T07:49:57.349Z
Learning: In the StorageError enum in common_sqlite/src/error.rs, the HexError variant should keep the manual From<HexError> implementation rather than using #[from] attribute, as it stores a String representation of the error rather than the HexError type itself.
Learnt from: hansieodendaal
PR: tari-project/tari#7123
File: comms/core/src/peer_manager/storage/database.rs:1517-1541
Timestamp: 2025-05-29T09:42:20.881Z
Learning: In the `hard_delete_all_stale_peers` method in `comms/core/src/peer_manager/storage/database.rs`, the SQL query intentionally uses exact equality (`peers.features = ?`) rather than bitwise operations (`peers.features & ? != 0`) when matching `COMMUNICATION_NODE` features. This is the intended behavior to match only peers with exactly the `COMMUNICATION_NODE` feature, excluding those with additional feature flags.
base_layer/core/src/transactions/transaction_components/unblinded_output.rs (1)
Learnt from: hansieodendaal
PR: tari-project/tari#7266
File: base_layer/core/src/chain_storage/blockchain_database.rs:457-461
Timestamp: 2025-06-26T13:18:55.898Z
Learning: The MinedInfo struct in the Tari blockchain database contains two optional fields: `input: Option<InputMinedInfo>` and `output: Option<OutputMinedInfo>`. This design allows methods returning MinedInfo to always return a valid struct rather than wrapping it in Option, with "not found" scenarios represented through the internal optional fields.
base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs (1)
Learnt from: hansieodendaal
PR: tari-project/tari#6974
File: base_layer/wallet/src/output_manager_service/service.rs:2026-2031
Timestamp: 2025-04-23T05:56:30.985Z
Learning: When setting the fee parameter in `output_to_self` method in the Tari wallet, use an accurate fee estimate instead of `fee_per_gram`. The `input_selection.as_final_fee()` method provides a good initial estimate, and the final fee can be obtained later from `stp.get_fee_amount()`.
integration_tests/src/ffi/wallet.rs (3)
Learnt from: hansieodendaal
PR: tari-project/tari#6974
File: base_layer/wallet/src/output_manager_service/service.rs:2026-2031
Timestamp: 2025-04-23T05:56:30.985Z
Learning: When setting the fee parameter in `output_to_self` method in the Tari wallet, use an accurate fee estimate instead of `fee_per_gram`. The `input_selection.as_final_fee()` method provides a good initial estimate, and the final fee can be obtained later from `stp.get_fee_amount()`.
Learnt from: SolfataraEmit
PR: tari-project/tari#6994
File: docs/src/API_GRPC_Explanation.md:236-244
Timestamp: 2025-04-28T11:41:15.722Z
Learning: The minotari_console_wallet reports a fourth field `timelocked_balance` in the terminal output for the `getBalance` method, which should be reflected in the documentation for accuracy. The `wallet.proto` file confirms that `timelocked_balance` is defined as the fourth field in the `GetBalanceResponse` message.
Learnt from: hansieodendaal
PR: tari-project/tari#7284
File: applications/minotari_console_wallet/src/automation/commands.rs:0-0
Timestamp: 2025-07-15T12:23:14.650Z
Learning: In applications/minotari_console_wallet/src/automation/commands.rs, the consistent error handling pattern for command execution is to use match statements that: 1) On success: log with debug!, print user feedback, and push tx_id to tx_ids vector for monitoring, 2) On error: print error message with eprintln! using the format "{CommandName} error! {}", rather than using .unwrap() which would panic.
applications/minotari_console_wallet/src/ui/state/tasks.rs (5)
Learnt from: SolfataraEmit
PR: tari-project/tari#6994
File: docs/src/API_GRPC_Explanation.md:236-244
Timestamp: 2025-04-28T11:41:15.722Z
Learning: The minotari_console_wallet reports a fourth field `timelocked_balance` in the terminal output for the `getBalance` method, which should be reflected in the documentation for accuracy.
Learnt from: hansieodendaal
PR: tari-project/tari#7284
File: applications/minotari_console_wallet/src/automation/commands.rs:0-0
Timestamp: 2025-07-15T12:23:14.650Z
Learning: In applications/minotari_console_wallet/src/automation/commands.rs, the consistent error handling pattern for command execution is to use match statements that: 1) On success: log with debug!, print user feedback, and push tx_id to tx_ids vector for monitoring, 2) On error: print error message with eprintln! using the format "{CommandName} error! {}", rather than using .unwrap() which would panic.
Learnt from: SolfataraEmit
PR: tari-project/tari#6994
File: docs/src/API_GRPC_Explanation.md:236-244
Timestamp: 2025-04-28T11:41:15.722Z
Learning: The minotari_console_wallet reports a fourth field `timelocked_balance` in the terminal output for the `getBalance` method, which should be reflected in the documentation for accuracy. The `wallet.proto` file confirms that `timelocked_balance` is defined as the fourth field in the `GetBalanceResponse` message.
Learnt from: hansieodendaal
PR: tari-project/tari#7266
File: base_layer/core/src/chain_storage/blockchain_database.rs:457-461
Timestamp: 2025-06-26T13:18:55.898Z
Learning: The MinedInfo struct in the Tari blockchain database contains two optional fields: `input: Option<InputMinedInfo>` and `output: Option<OutputMinedInfo>`. This design allows methods returning MinedInfo to always return a valid struct rather than wrapping it in Option, with "not found" scenarios represented through the internal optional fields.
Learnt from: hansieodendaal
PR: tari-project/tari#6974
File: base_layer/wallet/src/output_manager_service/service.rs:2026-2031
Timestamp: 2025-04-23T05:56:30.985Z
Learning: When setting the fee parameter in `output_to_self` method in the Tari wallet, use an accurate fee estimate instead of `fee_per_gram`. The `input_selection.as_final_fee()` method provides a good initial estimate, and the final fee can be obtained later from `stp.get_fee_amount()`.
integration_tests/src/wallet_ffi.rs (1)
Learnt from: hansieodendaal
PR: tari-project/tari#6974
File: base_layer/wallet/src/output_manager_service/service.rs:2026-2031
Timestamp: 2025-04-23T05:56:30.985Z
Learning: When setting the fee parameter in `output_to_self` method in the Tari wallet, use an accurate fee estimate instead of `fee_per_gram`. The `input_selection.as_final_fee()` method provides a good initial estimate, and the final fee can be obtained later from `stp.get_fee_amount()`.
integration_tests/tests/steps/wallet_steps.rs (4)

undefined

<retrieved_learning>
Learnt from: hansieodendaal
PR: #6963
File: common_sqlite/src/error.rs:88-92
Timestamp: 2025-05-23T07:49:57.349Z
Learning: In the StorageError enum in common_sqlite/src/error.rs, the HexError variant should keep the manual From implementation rather than using #[from] attribute, as it stores a String representation of the error rather than the HexError type itself.
</retrieved_learning>

<retrieved_learning>
Learnt from: hansieodendaal
PR: #7284
File: applications/minotari_console_wallet/src/automation/commands.rs:0-0
Timestamp: 2025-07-15T12:23:14.650Z
Learning: In applications/minotari_console_wallet/src/automation/commands.rs, the consistent error handling pattern for command execution is to use match statements that: 1) On success: log with debug!, print user feedback, and push tx_id to tx_ids vector for monitoring, 2) On error: print error message with eprintln! using the format "{CommandName} error! {}", rather than using .unwrap() which would panic.
</retrieved_learning>

<retrieved_learning>
Learnt from: hansieodendaal
PR: #7266
File: base_layer/core/src/chain_storage/blockchain_database.rs:457-461
Timestamp: 2025-06-26T13:18:55.898Z
Learning: The MinedInfo struct in the Tari blockchain database contains two optional fields: input: Option<InputMinedInfo> and output: Option<OutputMinedInfo>. This design allows methods returning MinedInfo to always return a valid struct rather than wrapping it in Option, with "not found" scenarios represented through the internal optional fields.
</retrieved_learning>

<retrieved_learning>
Learnt from: hansieodendaal
PR: #6974
File: base_layer/wallet/src/output_manager_service/service.rs:2026-2031
Timestamp: 2025-04-23T05:56:30.985Z
Learning: When setting the fee parameter in output_to_self method in the Tari wallet, use an accurate fee estimate instead of fee_per_gram. The input_selection.as_final_fee() method provides a good initial estimate, and the final fee can be obtained later from stp.get_fee_amount().
</retrieved_learning>

base_layer/common_types/src/tari_address/mod.rs (1)
Learnt from: hansieodendaal
PR: tari-project/tari#6963
File: comms/dht/src/proto/mod.rs:141-142
Timestamp: 2025-05-02T07:12:23.985Z
Learning: The `PeerFeatures::from_bits_u32_truncate` method truncates a u32 to u8 bits but can still return `None` if the resulting bits don't match any valid flags, making the error handling with `.ok_or_else()` necessary even after truncation.
base_layer/wallet/src/output_manager_service/service.rs (5)

undefined

<retrieved_learning>
Learnt from: hansieodendaal
PR: #6974
File: base_layer/wallet/src/output_manager_service/service.rs:2026-2031
Timestamp: 2025-04-23T05:56:30.985Z
Learning: When setting the fee parameter in output_to_self method in the Tari wallet, use an accurate fee estimate instead of fee_per_gram. The input_selection.as_final_fee() method provides a good initial estimate, and the final fee can be obtained later from stp.get_fee_amount().
</retrieved_learning>

<retrieved_learning>
Learnt from: hansieodendaal
PR: #7266
File: base_layer/core/src/chain_storage/blockchain_database.rs:457-461
Timestamp: 2025-06-26T13:18:55.898Z
Learning: The MinedInfo struct in the Tari blockchain database contains two optional fields: input: Option<InputMinedInfo> and output: Option<OutputMinedInfo>. This design allows methods returning MinedInfo to always return a valid struct rather than wrapping it in Option, with "not found" scenarios represented through the internal optional fields.
</retrieved_learning>

<retrieved_learning>
Learnt from: martinserts
PR: #7122
File: base_layer/wallet/src/transaction_service/offline_signing/one_sided_signer.rs:0-0
Timestamp: 2025-06-16T14:49:31.831Z
Learning: In Tari's one-sided transaction offline signing implementation, the script_keys vector in the script offset calculation should only include input.output.script_key_id, not output script keys, change output script keys, or recipient script keys. This is by design for the one-sided transaction protocol.
</retrieved_learning>

<retrieved_learning>
Learnt from: hansieodendaal
PR: #7284
File: applications/minotari_console_wallet/src/automation/commands.rs:0-0
Timestamp: 2025-07-15T12:23:14.650Z
Learning: In applications/minotari_console_wallet/src/automation/commands.rs, the consistent error handling pattern for command execution is to use match statements that: 1) On success: log with debug!, print user feedback, and push tx_id to tx_ids vector for monitoring, 2) On error: print error message with eprintln! using the format "{CommandName} error! {}", rather than using .unwrap() which would panic.
</retrieved_learning>

<retrieved_learning>
Learnt from: hansieodendaal
PR: #6963
File: comms/core/src/peer_manager/storage/database.rs:88-123
Timestamp: 2025-05-03T04:49:27.646Z
Learning: SQLite does not support the DEFAULT keyword in INSERT statements in the same way PostgreSQL does, which makes Diesel's batch insert API problematic when used with SQLite databases.
</retrieved_learning>

base_layer/core/src/transactions/transaction_components/memo_field.rs (1)
Learnt from: hansieodendaal
PR: tari-project/tari#7189
File: integration_tests/tests/steps/wallet_cli_steps.rs:464-467
Timestamp: 2025-06-07T06:49:32.174Z
Learning: In integration test code, using panic! for setup validation and prerequisite checking is acceptable and often preferred, as it provides immediate feedback when test scenarios are incorrectly configured rather than allowing tests to continue in invalid states.
base_layer/wallet/src/transaction_service/handle.rs (4)
Learnt from: hansieodendaal
PR: tari-project/tari#7266
File: base_layer/core/src/chain_storage/blockchain_database.rs:457-461
Timestamp: 2025-06-26T13:18:55.898Z
Learning: The MinedInfo struct in the Tari blockchain database contains two optional fields: `input: Option<InputMinedInfo>` and `output: Option<OutputMinedInfo>`. This design allows methods returning MinedInfo to always return a valid struct rather than wrapping it in Option, with "not found" scenarios represented through the internal optional fields.
Learnt from: hansieodendaal
PR: tari-project/tari#6974
File: base_layer/wallet/src/output_manager_service/service.rs:2026-2031
Timestamp: 2025-04-23T05:56:30.985Z
Learning: When setting the fee parameter in `output_to_self` method in the Tari wallet, use an accurate fee estimate instead of `fee_per_gram`. The `input_selection.as_final_fee()` method provides a good initial estimate, and the final fee can be obtained later from `stp.get_fee_amount()`.
Learnt from: martinserts
PR: tari-project/tari#7122
File: base_layer/wallet/src/transaction_service/offline_signing/one_sided_signer.rs:0-0
Timestamp: 2025-06-16T14:49:31.831Z
Learning: In Tari's one-sided transaction offline signing implementation, the script_keys vector in the script offset calculation should only include input.output.script_key_id, not output script keys, change output script keys, or recipient script keys. This is by design for the one-sided transaction protocol.
Learnt from: SWvheerden
PR: tari-project/tari#7300
File: base_layer/wallet/src/transaction_service/service.rs:1663-1663
Timestamp: 2025-07-10T13:21:46.666Z
Learning: In the Tari wallet codebase, scanned height persistence is handled by the UTXO scanner service, not individual services like TransactionService. Services that need current scanned height should read it via `get_last_scanned_height()` rather than persisting it themselves. This follows separation of concerns architecture pattern.
applications/minotari_console_wallet/src/automation/commands.rs (3)

undefined

<retrieved_learning>
Learnt from: hansieodendaal
PR: #7284
File: applications/minotari_console_wallet/src/automation/commands.rs:0-0
Timestamp: 2025-07-15T12:23:14.650Z
Learning: In applications/minotari_console_wallet/src/automation/commands.rs, the consistent error handling pattern for command execution is to use match statements that: 1) On success: log with debug!, print user feedback, and push tx_id to tx_ids vector for monitoring, 2) On error: print error message with eprintln! using the format "{CommandName} error! {}", rather than using .unwrap() which would panic.
</retrieved_learning>

<retrieved_learning>
Learnt from: hansieodendaal
PR: #7266
File: base_layer/core/src/chain_storage/blockchain_database.rs:457-461
Timestamp: 2025-06-26T13:18:55.898Z
Learning: The MinedInfo struct in the Tari blockchain database contains two optional fields: input: Option<InputMinedInfo> and output: Option<OutputMinedInfo>. This design allows methods returning MinedInfo to always return a valid struct rather than wrapping it in Option, with "not found" scenarios represented through the internal optional fields.
</retrieved_learning>

<retrieved_learning>
Learnt from: hansieodendaal
PR: #6974
File: base_layer/wallet/src/output_manager_service/service.rs:2026-2031
Timestamp: 2025-04-23T05:56:30.985Z
Learning: When setting the fee parameter in output_to_self method in the Tari wallet, use an accurate fee estimate instead of fee_per_gram. The input_selection.as_final_fee() method provides a good initial estimate, and the final fee can be obtained later from stp.get_fee_amount().
</retrieved_learning>

🧬 Code Graph Analysis (3)
base_layer/wallet_ffi/src/callback_handler_tests.rs (1)
base_layer/core/src/transactions/transaction_components/memo_field.rs (1)
  • open_from_string (901-908)
integration_tests/tests/steps/wallet_steps.rs (1)
base_layer/core/src/transactions/transaction_components/memo_field.rs (1)
  • open_from_string (901-908)
base_layer/common_types/src/tari_address/mod.rs (1)
base_layer/common_types/src/tari_address/dual_address.rs (1)
  • get_memo_field_payment_id_bytes (129-131)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Cucumber tests / FFI
  • GitHub Check: Cucumber tests / Base Layer
  • GitHub Check: test (testnet, esmeralda)
  • GitHub Check: test (mainnet, stagenet)
  • GitHub Check: test (nextnet, nextnet)
  • GitHub Check: ledger build tests
  • GitHub Check: cargo check with stable
  • GitHub Check: ci
🔇 Additional comments (80)
base_layer/wallet/src/output_manager_service/storage/models.rs (1)

32-32: LGTM: Clean migration from PaymentId to MemoField

The import and field type changes are consistent with the broader codebase migration from PaymentId to MemoField. The usage pattern remains the same (cloning from output.payment_id) which indicates good backward compatibility.

Also applies to: 59-59

base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs (1)

36-36: LGTM: Consistent PaymentId to MemoField migration

The import and method call changes are straightforward and maintain the same conversion logic. The from_bytes method signature appears to be preserved in the new MemoField implementation.

Also applies to: 110-110

applications/minotari_console_wallet/src/notifier/mod.rs (1)

336-336: LGTM: Updated method calls for MemoField API

The method name change from user_data_as_string() to payment_id_as_string() is more descriptive and aligns with the new MemoField API. The functionality remains the same - extracting the payment ID as a string for notification scripts.

Also applies to: 352-352

base_layer/wallet_ffi/src/callback_handler_tests.rs (1)

52-53: LGTM: Test code updated for MemoField API

The test code has been properly updated to use the new MemoField::open_from_string method with the required TxType::PaymentToOther parameter. The choice of PaymentToOther is appropriate for these test transactions, and all instances are updated consistently.

Also applies to: 325-325, 361-361, 381-381, 435-435, 471-471

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

33-33: LGTM: Updated method calls for MemoField API

The method name change from user_data_as_bytes() to payment_id_as_bytes() is consistent with the new MemoField API and provides clearer semantics. All three transaction types (Completed, Outbound, Inbound) are updated consistently in the gRPC event conversion.

Also applies to: 44-44, 55-55

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

82-82: LGTM: Method name update aligns with MemoField migration.

The change from user_data_as_bytes() to payment_id_as_bytes() is consistent with the broader migration from PaymentId to MemoField and provides clearer API naming.

integration_tests/tests/steps/wallet_ffi_steps.rs (3)

28-28: LGTM: Import updated for MemoField migration.

The import change from PaymentId to MemoField aligns with the codebase-wide migration to the new payment identifier type.


222-225: LGTM: Constructor usage updated consistently.

The change from PaymentId::open_from_string to MemoField::open_from_string maintains the same API pattern while using the new type, ensuring test compatibility with the migration.


235-238: LGTM: Consistent MemoField usage in one-sided transactions.

The constructor usage follows the same pattern as the previous change, maintaining consistency across both transaction types.

base_layer/core/src/transactions/transaction_components/unblinded_output.rs (2)

41-41: LGTM: Import updated for MemoField migration.

The import change reflects the migration from the payment_id module to the new memo_field module.


148-152: LGTM: Method signature updated for MemoField migration.

The parameter type change from PaymentId to MemoField is consistent with the codebase-wide migration. This represents a breaking change to the public API, which is expected for this migration.

base_layer/contacts/src/contacts_service/storage/sqlite_db.rs (1)

174-178: LGTM: Method calls updated for memo field API.

The changes from get_payment_id_user_data_bytes() to get_memo_field_payment_id_bytes() and with_payment_id_user_data() to with_memo_field_payment_id() are consistent with the new memo field API naming convention while preserving the same functionality.

integration_tests/src/wallet_ffi.rs (2)

33-33: LGTM: Import updated for MemoField migration.

The import change from PaymentId to MemoField aligns with the codebase-wide type migration.


161-173: LGTM: FFI method signature updated consistently.

The parameter type change from PaymentId to MemoField in the send_transaction method maintains API consistency with the underlying wallet implementation while adapting the FFI layer to the new type.

base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs (3)

41-41: LGTM! Import updated correctly for MemoField refactor.

The import change from PaymentId to MemoField is correct and aligns with the broader refactoring effort to enforce size validation on payment identifiers.


95-95: LGTM! Struct field type updated correctly.

The field type change from PaymentId to MemoField maintains the same semantic meaning while adding enhanced validation capabilities. The field name remains consistent, ensuring interface compatibility.


121-121: LGTM! Constructor parameter type updated consistently.

The parameter type change from PaymentId to MemoField maintains consistency with the struct field definition and import changes. The direct assignment on line 138 will work correctly with the new type.

integration_tests/tests/steps/wallet_steps.rs (8)

59-59: LGTM: Import updated to support MemoField migration.

The import statement correctly updates to use MemoField and TxType from the new memo_field module, supporting the broader refactor from PaymentId to MemoField.


818-827: LGTM: Consistent MemoField usage in one-sided transaction.

The replacement of PaymentId::open_from_string with MemoField::open_from_string maintains the same API signature and behavior. The transaction type TxType::PaymentToOther remains unchanged, ensuring semantic consistency.


884-893: LGTM: Proper MemoField integration in one-sided transaction.

The migration correctly preserves the payment ID string formatting and transaction type semantics while adopting the new MemoField abstraction.


997-1007: LGTM: Interactive transaction properly migrated to MemoField.

The change maintains the existing message format and uses the appropriate TxType::PaymentToOther for interactive transactions between wallets.


1114-1124: LGTM: Consistent MemoField usage in multi-transaction scenario.

The migration properly handles the bulk transaction case with the same pattern used throughout the file.


2197-2201: LGTM: Self-transfer correctly uses TxType::PaymentToSelf.

The migration appropriately uses TxType::PaymentToSelf for transactions where the sender and receiver are the same wallet, demonstrating proper transaction type classification.


2776-2780: LGTM: Import operations properly migrated with appropriate transaction type.

The UTXO import operation correctly uses TxType::ImportedUtxoNoneRewindable, maintaining the semantic meaning while adopting the new MemoField structure.


3291-3291: LGTM: Burn transaction correctly migrated to MemoField.

The burn transaction appropriately uses TxType::Burn with a simple message, demonstrating the migration covers all transaction types in the integration tests.

applications/minotari_console_wallet/src/ui/state/tasks.rs (3)

38-38: LGTM: Import updated for MemoField migration.

The import correctly switches from PaymentId to MemoField as part of the coordinated migration across the codebase.


57-57: LGTM: Function parameter type updated correctly.

The function signature properly adopts MemoField while maintaining the same functionality. The parameter is passed through to the transaction service which has been correspondingly updated.


111-111: LGTM: Function parameter type updated correctly.

The burn transaction function signature properly adopts MemoField while maintaining the same functionality. The parameter is passed through to the transaction service which has been correspondingly updated.

integration_tests/src/ffi/wallet.rs (3)

34-34: LGTM: Import updated for MemoField migration.

The import correctly switches from PaymentId to MemoField as part of the coordinated migration across the codebase.


356-356: LGTM: Function parameter type updated correctly.

The function signature properly adopts MemoField for the payment ID parameter, maintaining the same FFI interface functionality.


369-369: LGTM: Method call updated to match MemoField API.

The method call correctly switches from user_data_as_string() to payment_id_as_string() to match the new MemoField API, maintaining the same string conversion functionality for FFI purposes.

base_layer/common_types/src/tari_address/mod.rs (3)

50-50: LGTM: Constant made public for MemoField validation.

Making MAX_ENCRYPTED_DATA_SIZE public is appropriate as it's likely needed by the new MemoField implementation for size validation across modules.


245-247: LGTM: Method renamed for consistency with MemoField terminology.

The method name correctly changes from get_payment_id_user_data_bytes to get_memo_field_payment_id_bytes and the internal call to DualAddress is updated accordingly, maintaining the same functionality while adopting consistent terminology.


252-256: LGTM: Method renamed for consistency with MemoField terminology.

The method name correctly changes from with_payment_id_user_data to with_memo_field_payment_id and the internal call to add_memo_field_payment_id is updated accordingly, maintaining the same functionality while adopting consistent terminology.

base_layer/core/src/transactions/transaction_key_manager/inner.rs (4)

106-106: LGTM: Import update aligns with PaymentId to MemoField migration.

The import change from PaymentId to MemoField is consistent with the comprehensive refactor described in the PR objectives.


1625-1625: LGTM: Parameter type updated for enhanced validation.

The change from PaymentId to MemoField parameter maintains interface compatibility while adding the 256-byte size validation mentioned in the PR objectives.


1650-1650: LGTM: Return type updated for consistency with migration.

The return type change from PaymentId to MemoField is consistent with the systematic migration and maintains the same semantic meaning.


1665-1665: LGTM: Tuple return type updated for migration consistency.

The return type change from PaymentId to MemoField in the tuple maintains the same interface contract while providing enhanced validation capabilities.

base_layer/core/src/transactions/transaction_key_manager/wrapper.rs (4)

41-41: LGTM: Import update consistent with migration.

The import change from PaymentId to MemoField aligns with the comprehensive refactor and is consistent with the changes in the inner implementation.


388-388: LGTM: Parameter type updated to match interface.

The parameter type change from PaymentId to MemoField maintains consistency with the inner implementation and provides enhanced validation.


402-402: LGTM: Return type updated for interface consistency.

The return type change from PaymentId to MemoField maintains consistency with the inner implementation and the overall migration strategy.


415-415: LGTM: Tuple return type updated for migration completion.

The return type change from PaymentId to MemoField in the tuple completes the consistent migration pattern across both the wrapper and inner implementations.

base_layer/core/src/transactions/transaction_key_manager/interface.rs (3)

47-47: LGTM: Import change aligns with the MemoField refactor.

The import update from PaymentId to MemoField is consistent with the broader refactor to introduce size validation for payment identifiers.


377-377: LGTM: Method signature correctly updated to use MemoField.

The parameter type change from PaymentId to MemoField maintains the same semantics while adding size validation as intended by the refactor.


392-392: LGTM: Tuple return type correctly updated with MemoField.

The return type change maintains the logical structure while incorporating the new MemoField type for the payment identifier component.

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

22-22: LGTM - Clean import removal.

The removal of TryInto from imports is appropriate as it's no longer needed with the MemoField refactor.


58-58: LGTM - Required imports for MemoField refactor.

The addition of MemoField and TxType imports is necessary for the PaymentId to MemoField migration.


313-314: Excellent error handling improvement.

Good to see the .unwrap() has been replaced with proper error handling that maps to OutputManagerError::ServiceError with a descriptive message. This follows the established error handling patterns.


851-858: Good error handling with relevant context.

The error mapping includes size information ("Invalid payment ID, size: {}") which is particularly relevant for this PR focused on payment ID size validation. The proper error handling replaces the previous .unwrap() usage.


1030-1030: LGTM - Type migration to MemoField.

The parameter type change from PaymentId to MemoField is consistent with the broader refactor.


1152-1152: LGTM - Consistent type migration.

Another appropriate parameter type change from PaymentId to MemoField.


1278-1283: Excellent API migration with proper error handling.

The replacement of pattern matching on PaymentId variants with MemoField::get_u64_data() accessor method is clean. The error handling includes transaction ID context which aids in debugging.


1312-1312: LGTM - Parameter type updated.

The tx_payment_id parameter type change to MemoField is consistent with the refactor.


1894-1901: Good use of MemoField API with proper error handling.

The add_sender_address method call demonstrates the new MemoField builder-style API. The error mapping to OutputManagerError::InvalidPaymentIdFormat is appropriate for the context.


1966-1966: LGTM - Function parameter type updated.

The payment_id parameter type change to MemoField maintains consistency with the refactor.


2043-2046: Appropriate MemoField constructor usage.

The MemoField::open_from_string constructor with transaction type PaymentToSelf is well-suited for pay-to-self transactions.


2798-2798: LGTM - Parameter type migration.

The payment_id parameter type change to MemoField is consistent with the overall refactor.


2807-2814: Consistent MemoField API usage.

The add_sender_address method call follows the same pattern as other usages with appropriate error handling.


2882-2882: LGTM - Function parameter type updated.

The payment_id parameter type change to MemoField maintains consistency.


2493-2499: Good descriptive MemoField usage.

The MemoField::open_from_string constructor with a descriptive message about coin splitting and appropriate TxType::CoinSplit provides clear context for the operation.


2527-2527: Consistent coin split MemoField usage.

The MemoField::open_from_string constructor maintains consistency with other coin split operations.


2664-2667: Appropriate MemoField constructor for coin split.

The MemoField::open_from_string usage for coin split operations is consistent with established patterns.


3027-3027: Appropriate MemoField for wallet scraping.

The MemoField::open_from_string constructor with "scraping wallet" description and TxType::PaymentToOther is appropriate for this operation.


3124-3127: Appropriate MemoField for atomic swap claim.

The MemoField::open_from_string constructor with "SHA-XTR atomic swap" description and TxType::ClaimAtomicSwap is well-suited for this specialized operation.


3207-3210: Appropriate MemoField for atomic swap refund.

The MemoField::open_from_string constructor with "SHA-XTR atomic refund" description and TxType::HtlcAtomicSwapRefund correctly categorizes this refund operation.

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

45-45: Import statement correctly updated for the MemoField refactor.

The import of MemoField is properly aligned with the type substitution throughout the file.


107-236: Enum variant type substitutions are consistent and correct.

All PaymentId types have been properly replaced with MemoField across the TransactionServiceRequest enum variants. The changes maintain type consistency and align with the refactor objectives.


765-1471: Method signatures consistently updated with MemoField type.

All public API methods in TransactionServiceHandle have been properly updated to use MemoField instead of PaymentId. The changes maintain API consistency while introducing the new size validation constraints.


1-1589: Comprehensive and well-executed type refactor.

The migration from PaymentId to MemoField is thorough and consistent throughout the file. This change successfully introduces size validation constraints (256 bytes) while maintaining API compatibility and follows the architectural goals outlined in the PR objectives.

base_layer/core/src/transactions/transaction_components/memo_field.rs (1)

1406-1459: Excellent security test for payment ID confusion

The test_payment_id_parsing_confusion test is well-designed to catch potential security issues where a crafted Open payment ID could be misinterpreted as a different variant during deserialization. This type of proactive security testing is commendable.

applications/minotari_console_wallet/src/automation/commands.rs (10)

90-90: LGTM! Import update aligns with the refactor.

The import change from PaymentId to MemoField and TxType is correct and necessary for the type migration. The addition of TxType indicates proper support for transaction-specific validation in the new MemoField API.


167-167: Function signature correctly updated for new type.

The parameter type change from PaymentId to MemoField maintains the same semantic meaning while enabling the new validation capabilities as per the PR objectives.


186-186: Consistent parameter type update.

The burn_tari function parameter type change follows the same pattern as other functions in this refactor, maintaining consistency across the codebase.


217-217: Comprehensive parameter type updates across transaction functions.

All transaction-related functions consistently update their payment_id parameters from PaymentId to MemoField. This systematic approach ensures type safety and validation consistency across the entire transaction API surface.

Also applies to: 254-254, 306-306, 322-322, 339-339, 360-360, 384-384, 403-403


471-471: Method call updated to use new API.

The method call change from user_data_as_string() to payment_id_as_string() uses the new MemoField API and provides clearer semantic meaning for the operation.


497-497: Consistent method call update.

The method call follows the same pattern as other updates in this file, maintaining consistency in the new API usage.


616-616: Method call consistently updated.

The method call change maintains consistency with the new MemoField API throughout the file.


763-763: Constructor calls properly updated with transaction type context.

The MemoField::open_from_string constructor calls correctly replace PaymentId::open_from_string and include appropriate transaction type information. The use of TxType::Burn for burn transactions and detect_tx_metadata() for address-based type detection shows proper validation context is being provided.

Also applies to: 942-945, 1304-1307


1813-1813: Additional constructor calls properly updated.

These MemoField::open_from_string calls maintain the same pattern, using detect_tx_metadata() for automatic type detection and explicit TxType::CoinSplit where appropriate. The transaction type context enables proper validation.

Also applies to: 1831-1831, 1858-1858, 1870-1870


2030-2030: Specialized transaction type categorization in constructor calls.

The MemoField::open_from_string calls use appropriate specialized transaction types (TxType::ClaimAtomicSwap, TxType::HtlcAtomicSwapRefund, TxType::ValidatorNodeRegistration) for different operations. This granular categorization enables proper validation specific to each transaction type.

Also applies to: 2053-2053, 2073-2073, 2106-2106, 2512-2512

Comment on lines +861 to +878
// we dont know where the tari address ends and the user data starts, so we need to find it using the checksum
fn find_tari_address(bytes: &[u8]) -> Result<(TariAddress, usize), String> {
if bytes.len() < TARI_ADDRESS_INTERNAL_SINGLE_SIZE {
return Err("Not enough bytes for single TariAddress".to_string());
}
// Now we have to try and brute force a match here
let mut offset = 0;
while (TARI_ADDRESS_INTERNAL_DUAL_SIZE + offset) <= bytes.len() {
if let Ok(address) = TariAddress::from_bytes(&bytes[..(TARI_ADDRESS_INTERNAL_DUAL_SIZE + offset)]) {
return Ok((address, TARI_ADDRESS_INTERNAL_DUAL_SIZE + offset));
}
offset += 1;
}
if let Ok(address) = TariAddress::from_bytes(&bytes[..TARI_ADDRESS_INTERNAL_SINGLE_SIZE]) {
return Ok((address, TARI_ADDRESS_INTERNAL_SINGLE_SIZE));
}
Err("No valid TariAddress found".to_string())
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

❓ Verification inconclusive

Consider optimizing the TariAddress finding algorithm

The find_tari_address method uses a brute force approach with potentially O(n) iterations. While this might be necessary for backward compatibility, consider documenting the performance implications and any alternatives that were considered.


🏁 Script executed:

#!/bin/bash
# Check if there are any performance benchmarks or tests for find_tari_address
rg -A 5 "benchmark.*find_tari_address|bench.*find_tari_address|perf.*find_tari_address" --type rust

Length of output: 101


Add performance benchmarks and document implications for find_tari_address

I didn’t find any existing benchmarks or performance tests for find_tari_address. Since it uses a brute-force, O(n) approach, please:

  • Document the expected performance characteristics (e.g., average/maximum byte lengths, typical iteration counts).
  • Outline any alternative strategies you considered (such as indexing or early exits) and why the current approach was chosen (e.g., backward compatibility).
  • (Optional but recommended) Add a simple benchmark or unit test measuring its runtime for representative inputs, so future changes can be evaluated against a baseline.

This will help readers understand the trade-offs and make future optimizations easier.

🤖 Prompt for AI Agents
In base_layer/core/src/transactions/transaction_components/memo_field.rs around
lines 861 to 878, the find_tari_address function lacks documentation on its
performance and no benchmarks exist. Add comments describing its O(n)
brute-force approach, typical input sizes, and iteration counts. Explain
alternative methods considered and why this approach was chosen, such as
backward compatibility. Additionally, create a benchmark or unit test to measure
runtime on representative inputs to establish a performance baseline for future
changes.

Copy link
Copy Markdown
Collaborator

@SWvheerden SWvheerden left a comment

Choose a reason for hiding this comment

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

Looking very good, just two unchecked functions we need to ensure are fine

}

/// Helper function to set the 'recipient_address' of a 'PaymentId::TransactionInfo'
pub fn transaction_info_set_address(&mut self, address: TariAddress) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this can make the tx larger, this needs to check the size

}
}

pub fn transaction_info_set_sent_output_hashes(&mut self, sent_output_hashes: Vec<FixedHash>) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

same here, this adds 32 bytes for each hash

@SWvheerden SWvheerden merged commit c1c7ecb into tari-project:development Jul 22, 2025
15 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Aug 1, 2025
4 tasks
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.

ensure safe Payment id size

2 participants