Skip to content

feat!: adds UTXO memos to encrypted data and wallet - #1595

Merged
sdbondi merged 4 commits into
tari-project:developmentfrom
sdbondi:encrypted-memo
Oct 10, 2025
Merged

feat!: adds UTXO memos to encrypted data and wallet#1595
sdbondi merged 4 commits into
tari-project:developmentfrom
sdbondi:encrypted-memo

Conversation

@sdbondi

@sdbondi sdbondi commented Oct 10, 2025

Copy link
Copy Markdown
Member

Description

feat!: adds UTXO memos to encrypted data and wallet
feat(wallet/webui): adds support for sending a memo message when performing stealth and confidential transfers

Motivation and Context

An optional UTXO memo is encrypted in the existing encrypted data for a UTXO. This allows senders to include an arbitrary 253 byte message (invoice number, saying hello etc) for the recipient.

How Has This Been Tested?

Manually, additional low-level unit tests

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

Breaking Changes

  • None
  • Requires data directory to be deleted
  • Other - Please specify

Summary by CodeRabbit

  • New Features

    • Optional memos for confidential and stealth transfers across CLI, SDK, wallet daemon, and web UI (message or bytes, validated).
    • Web UI: memo input in send flow, Memo display component, UTXO/Stealth lists show memos.
    • Wallet daemon & clients persist and return memos for outputs and UTXO queries; APIs and bindings accept/expose memo fields.
  • Chores

    • Updated a third-party dependency to a newer version.

@coderabbitai

coderabbitai Bot commented Oct 10, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds optional memos to confidential and stealth outputs across crypto, SDK, daemon, client bindings, storage, CLI, and web UI; refactors EncryptedData into template_lib_types backed by MaxBytes/MaxString; updates encryption/decryption APIs to carry memos and DecryptedData; persists memos to SQLite and updates tests.

Changes

Cohort / File(s) Summary
Workspace deps
Cargo.toml, crates/wallet/crypto/Cargo.toml, crates/wallet/sdk/Cargo.toml
Bump thiserror; add argon2, optional serde/ts-rs to wallet crypto; enable serde feature for wallet SDK crypto dependency; remove a dev-dep.
Template types & utilities
crates/template_lib_types/src/{encrypted_data.rs,max_bytes.rs,max_string.rs,lib.rs,serde_helpers.rs}, crates/template_lib/src/{models/mod.rs,models/unspent_output.rs,prelude.rs}
Move/enhance EncryptedData into template_lib_types using MaxBytes, add MaxBytes/MaxString types, update serde helpers, adjust prelude re-exports, and remove models::encrypted_data re-export.
Engine & p2p imports
crates/engine_types/src/{confidential/claim.rs,crypto/output.rs}, crates/p2p/src/conversions/transaction.rs
Redirect EncryptedData and crypto byte types to new tari_template_lib_types/types paths; no logic changes.
Wallet crypto (core)
crates/wallet/crypto/src/{lib.rs,memo.rs,encrypted_data.rs,unblinded_statement.rs,error.rs,confidential.rs,stealth.rs}, crates/wallet/crypto/tests/*
Add memo module and Memo type; introduce DecryptedData; change encrypt/decrypt/unblind APIs to accept/return memo and skip_memo; add error variants; update tests.
Wallet SDK APIs & models
crates/wallet/sdk/src/apis/{confidential_crypto.rs,confidential_outputs.rs,confidential_transfer.rs,stealth_crypto.rs,stealth_outputs.rs,stealth_transfer.rs}, crates/wallet/sdk/src/models/{confidential_output.rs,stealth_output.rs}
Thread memo through proof/transfer flows; update signatures to accept/return memo/DecryptedData; add memo to output models; switch EncryptedData imports to types.
Daemon handlers
applications/tari_walletd/src/handlers/{accounts.rs,confidential.rs,stealth_utxos.rs}
Propagate memos into encryption/decryption and transfer params; use decrypted.value(); add memo in UTXO responses and claim paths.
CLI
applications/tari_wallet_cli/src/command/transaction.rs
Add optional --memo_message / -m flag to confidential transfers and forward memo in request.
Bindings & client types
bindings/src/{index.ts,types/Memo.ts,types/wallet-daemon-client/*.ts}, clients/wallet_daemon_client/src/types.rs
Add Memo type; re-export in bindings; add memo/output_memo to request and response types (e.g., ConfidentialTransferRequest, StealthTransferRequest, UtxoInfo, ProofsGenerateRequest).
Web UI
applications/tari_walletd/web_ui/src/components/Memo.tsx, .../routes/AssetVault/Tokens/{components/SendMoney.tsx,steps/FormStep.tsx}, .../routes/StealthUtxoList/StealthUtxoList.tsx, .../services/api/hooks/useAccounts.ts
New Memo React component; add memo field to send form; include output_memo in API calls; display memo column in stealth UTXO list; update hooks types.
Storage (SQLite)
crates/wallet/storage_sqlite/migrations/.../up.sql, crates/wallet/storage_sqlite/src/{schema.rs,models/{confidential_output.rs,stealth_output.rs},writer.rs}
Add nullable memo_json columns to confidential_outputs and stealth_outputs; persist/deserialize memo JSON to/from DB; update imports.
SDK/Wallet tests & integration
crates/wallet/sdk/tests/*, integration_tests/*, crates/wallet/sdk/tests/base_layer_compat.rs
Add memo-aware tests (compatibility, encryption round-trips), update test imports, include memo: None in test initializations, and include explicit None memo fields in integration requests.
Misc & cleanups
applications/tari_swarm_daemon/.../minotari_wallet.rs, crates/template_test_tooling/src/support/{confidential.rs,stealth.rs}, crates/template_abi/src/rust.rs
Import path cleanups to types; test-tooling adjustments; remove std::io re-export from template_abi rust std list.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as CLI/Web UI
  participant Daemon
  participant SDK
  participant Crypto as Wallet Crypto
  participant DB

  User->>UI: Initiate transfer (optional memo)
  UI->>Daemon: TransferRequest { ..., memo/output_memo }
  Daemon->>SDK: Build params { ..., memo }
  SDK->>Crypto: encrypt_data(amount, mask, pk, sk, memo)
  Crypto-->>SDK: EncryptedData
  SDK->>Daemon: Output statements { memo }
  Daemon->>DB: Persist outputs (memo_json)
  DB-->>Daemon: OK
  Daemon-->>UI: Response (includes memo)
Loading
sequenceDiagram
  autonumber
  participant Owner
  participant Daemon
  participant SDK
  participant Crypto
  participant DB

  Owner->>Daemon: List outputs
  Daemon->>DB: Load outputs (memo_json)
  DB-->>Daemon: Outputs + memo_json
  Daemon->>SDK: decrypt_unblind(..., skip_memo=false)
  SDK->>Crypto: decrypt_data(enc_key, commitment, encrypted_data, false)
  Crypto-->>SDK: DecryptedData { value, mask, memo? }
  SDK-->>Daemon: Models { memo propagated }
  Daemon-->>Owner: UtxoInfo/OutputModel { memo }
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

I tuck a note in every hop,
A tiny memo, never stop.
Through encrypt, decrypt, and DB,
I carry whispers, safe and free.
A rabbit signs with carrot grin—memories snug within. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title clearly and concisely describes the main change of adding UTXO memos to encrypted data and wallet support, aligning with the PR objectives and indicating a breaking feature with proper Conventional Commit notation.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/template_lib_types/src/encrypted_data.rs (1)

20-24: Missing import for size_of

size_of::() requires an import. The diff above adds use tari_template_abi::rust::mem::size_of, which is consistent with no_std usage in this crate. Without it, this will not compile.

🧹 Nitpick comments (9)
crates/template_lib_types/src/serde_helpers.rs (1)

18-29: LGTM! Consider adding #[must_use] attributes.

The implementation is correct and follows the expected pattern for query methods. Both methods are properly marked as const fn, enabling compile-time evaluation.

Consider adding #[must_use] attributes to both methods to align with Rust standard library conventions (e.g., Vec::len(), Vec::is_empty()). This prevents accidental misuse and satisfies clippy's must_use_candidate lint:

 impl<'a> BytesCow<'a> {
+    #[must_use]
     pub const fn len(&self) -> usize {
         match self {
             BytesCow::Borrowed(v) => v.len(),
             BytesCow::Owned(v) => v.len(),
         }
     }

+    #[must_use]
     pub const fn is_empty(&self) -> bool {
         self.len() == 0
     }
 }
crates/wallet/storage_sqlite/migrations/2023-02-08-122514_initial/up.sql (1)

155-155: Consider adding a CHECK constraint for memo size validation.

The PR description mentions a 253-byte limit for memos, but the memo_json columns are defined as TEXT NULL without size constraints. While SQLite TEXT can store very large values and the limit is likely enforced at the application layer, adding a database-level CHECK constraint would provide defense-in-depth validation.

For example:

memo_json TEXT NULL CHECK(length(memo_json) IS NULL OR length(memo_json) <= 512),

Note: The constraint should account for JSON serialization overhead, so 512 bytes provides headroom for a 253-byte memo plus JSON structure.

Also applies to: 249-249

bindings/src/types/wallet-daemon-client/UtxoInfo.ts (1)

3-3: LGTM: Memo support added to UtxoInfo.

The import and optional field addition correctly integrate memo support.

Note: The TypeScript pattern memo?: Memo | null is redundant since ?: already makes the field optional. However, this is consistent with other types in the codebase and does not cause issues.

Also applies to: 11-11

bindings/src/types/wallet-daemon-client/ProofsGenerateRequest.ts (1)

3-3: LGTM: Memo support added to ProofsGenerateRequest.

The import and optional field addition correctly enable memo data in proof generation requests.

Note: The pattern memo?: Memo | null is redundant (see comment on UtxoInfo.ts) but consistent with the codebase style.

Also applies to: 14-14

crates/wallet/crypto/tests/output_statement.rs (1)

124-130: Consider using a named helper for sender_public_nonce.

The inline key pair generation works correctly, but replacing test_sender_public_nonce() with create_key_pair_from_seed(0) makes the intent slightly less clear. If test_sender_public_nonce() is no longer available, consider extracting this to a local helper function in the test module for better readability.

Apply this diff to improve clarity:

+fn test_sender_public_nonce() -> RistrettoPublicKey {
+    let (_sk, pk) = create_key_pair_from_seed(0);
+    pk
+}
+
 fn make_output_statements<A: Into<Amount> + Copy>(amounts: &[A]) -> Vec<UnblindedStealthOutputStatement> {
     amounts
         .iter()
         .map(|&amount| {
             // ... existing code ...
             let statement = UnblindedOutputStatement {
                 amount,
                 mask: output_mask,
                 resource_view_key: None,
-                sender_public_nonce: {
-                    let (_sk, pk) = create_key_pair_from_seed(0);
-                    pk
-                },
+                sender_public_nonce: test_sender_public_nonce(),
                 minimum_value_promise: 0,
                 encrypted_data: EncryptedData::try_from(vec![0; EncryptedData::min_size()]).unwrap(),
                 memo: None,
             };
applications/tari_walletd/src/handlers/confidential.rs (1)

254-267: TODO: Memo support not yet implemented for output proofs.

The TODO comment indicates that memo support is not yet available in the output proof path. This is explicitly acknowledged by the developer.

Would you like me to open a tracking issue for implementing memo support in handle_create_output_proof?

crates/template_lib_types/src/memo.rs (3)

37-45: Small ergonomics: provide fallible TryFrom constructors

new_message/new_bytes return Option. Consider implementing TryFrom<&str>/TryFrom<&[u8]> (or dedicated try_new_*) returning a Result with an error describing overflow. Aids caller diagnostics.

Also applies to: 47-51


78-85: Encoding/decoding choices are efficient; minor doc fix

Compact 1-byte length is good. Unknown-tag fallback preserves payload—nice forward-compat choice.

Nit: comment says “255) - 1 (enum tag + length (u8))” but you subtract 2 bytes (tag+len) to get 253. Fix comment for clarity.

-    /// EncryptedData memo size (255) - 1 (enum tag + length (u8))
+    /// EncryptedData memo size (255) - 2 (enum tag + length (u8)) = 253 bytes usable

Also applies to: 100-132


229-236: Test name nit

it_borsh_encodes_to_max_bytes uses custom encode_into (not borsh). Rename to avoid confusion.

-    fn it_borsh_encodes_to_max_bytes() {
+    fn it_encodes_within_encrypted_data_payload_limit() {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70f1f28 and 3b53965.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (59)
  • Cargo.toml (1 hunks)
  • applications/tari_swarm_daemon/src/process_manager/processes/minotari_wallet.rs (1 hunks)
  • applications/tari_wallet_cli/src/command/transaction.rs (4 hunks)
  • applications/tari_walletd/src/handlers/accounts.rs (7 hunks)
  • applications/tari_walletd/src/handlers/confidential.rs (7 hunks)
  • applications/tari_walletd/src/handlers/stealth_utxos.rs (1 hunks)
  • applications/tari_walletd/web_ui/src/components/Memo.tsx (1 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx (2 hunks)
  • applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/FormStep.tsx (2 hunks)
  • applications/tari_walletd/web_ui/src/routes/StealthUtxoList/StealthUtxoList.tsx (4 hunks)
  • applications/tari_walletd/web_ui/src/services/api/hooks/useAccounts.ts (4 hunks)
  • bindings/src/index.ts (1 hunks)
  • bindings/src/types/Memo.ts (1 hunks)
  • bindings/src/types/wallet-daemon-client/ConfidentialTransferRequest.ts (2 hunks)
  • bindings/src/types/wallet-daemon-client/ProofsGenerateRequest.ts (2 hunks)
  • bindings/src/types/wallet-daemon-client/StealthTransferRequest.ts (2 hunks)
  • bindings/src/types/wallet-daemon-client/UtxoInfo.ts (1 hunks)
  • clients/wallet_daemon_client/src/types.rs (5 hunks)
  • crates/engine_types/src/confidential/claim.rs (1 hunks)
  • crates/engine_types/src/crypto/output.rs (1 hunks)
  • crates/p2p/src/conversions/transaction.rs (1 hunks)
  • crates/template_lib/src/models/mod.rs (0 hunks)
  • crates/template_lib/src/models/unspent_output.rs (1 hunks)
  • crates/template_lib/src/prelude.rs (1 hunks)
  • crates/template_lib_types/src/encrypted_data.rs (3 hunks)
  • crates/template_lib_types/src/lib.rs (1 hunks)
  • crates/template_lib_types/src/max_bytes.rs (1 hunks)
  • crates/template_lib_types/src/max_string.rs (1 hunks)
  • crates/template_lib_types/src/memo.rs (1 hunks)
  • crates/template_lib_types/src/serde_helpers.rs (1 hunks)
  • crates/template_test_tooling/src/support/confidential.rs (5 hunks)
  • crates/template_test_tooling/src/support/stealth.rs (3 hunks)
  • crates/wallet/crypto/Cargo.toml (0 hunks)
  • crates/wallet/crypto/src/confidential.rs (2 hunks)
  • crates/wallet/crypto/src/encrypted_data.rs (6 hunks)
  • crates/wallet/crypto/src/error.rs (1 hunks)
  • crates/wallet/crypto/src/stealth.rs (2 hunks)
  • crates/wallet/crypto/src/unblinded_statement.rs (3 hunks)
  • crates/wallet/crypto/tests/output_statement.rs (3 hunks)
  • crates/wallet/crypto/tests/viewable_balance_proof.rs (2 hunks)
  • crates/wallet/sdk/src/apis/confidential_crypto.rs (3 hunks)
  • crates/wallet/sdk/src/apis/confidential_outputs.rs (3 hunks)
  • crates/wallet/sdk/src/apis/confidential_transfer.rs (8 hunks)
  • crates/wallet/sdk/src/apis/stealth_crypto.rs (3 hunks)
  • crates/wallet/sdk/src/apis/stealth_outputs.rs (5 hunks)
  • crates/wallet/sdk/src/apis/stealth_transfer.rs (9 hunks)
  • crates/wallet/sdk/src/models/confidential_output.rs (2 hunks)
  • crates/wallet/sdk/src/models/stealth_output.rs (2 hunks)
  • crates/wallet/sdk/tests/base_layer_compat.rs (1 hunks)
  • crates/wallet/sdk/tests/confidential_output_api.rs (2 hunks)
  • crates/wallet/sdk/tests/crypto_api.rs (2 hunks)
  • crates/wallet/sdk/tests/support/harness.rs (2 hunks)
  • crates/wallet/storage_sqlite/migrations/2023-02-08-122514_initial/up.sql (2 hunks)
  • crates/wallet/storage_sqlite/src/models/confidential_output.rs (3 hunks)
  • crates/wallet/storage_sqlite/src/models/stealth_output.rs (3 hunks)
  • crates/wallet/storage_sqlite/src/schema.rs (3 hunks)
  • crates/wallet/storage_sqlite/src/writer.rs (4 hunks)
  • integration_tests/src/wallet_daemon_client.rs (2 hunks)
  • integration_tests/tests/steps/wallet.rs (1 hunks)
💤 Files with no reviewable changes (2)
  • crates/wallet/crypto/Cargo.toml
  • crates/template_lib/src/models/mod.rs
🧰 Additional context used
🧬 Code graph analysis (46)
bindings/src/types/Memo.ts (1)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/wallet-daemon-client/ProofsGenerateRequest.ts (2)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/sdk/tests/support/harness.rs (1)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
bindings/src/types/wallet-daemon-client/ConfidentialTransferRequest.ts (2)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
integration_tests/tests/steps/wallet.rs (1)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/wallet/crypto/src/unblinded_statement.rs (7)
bindings/src/types/UtxoTag.ts (1)
  • UtxoTag (8-8)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/p2p/src/conversions/transaction.rs (2)
  • value (807-811)
  • value (865-869)
crates/wallet/sdk/src/apis/stealth_transfer.rs (1)
  • value (949-951)
crates/wallet/sdk/tests/base_layer_compat.rs (1)
crates/template_lib_types/src/memo.rs (2)
  • decode_from (100-132)
  • new_bytes (47-51)
crates/template_lib/src/models/unspent_output.rs (5)
bindings/src/types/PedersenCommitmentBytes.ts (1)
  • PedersenCommitmentBytes (6-6)
bindings/src/types/RistrettoPublicKeyBytes.ts (1)
  • RistrettoPublicKeyBytes (6-6)
bindings/src/types/UtxoTag.ts (1)
  • UtxoTag (8-8)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
bindings/src/types/ViewableBalanceProof.ts (1)
  • ViewableBalanceProof (27-62)
applications/tari_walletd/web_ui/src/services/api/hooks/useAccounts.ts (2)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/storage_sqlite/src/schema.rs (2)
crates/wallet/storage_sqlite/src/writer.rs (14)
  • stealth_outputs (1065-1081)
  • stealth_outputs (1151-1151)
  • stealth_outputs (1152-1152)
  • stealth_outputs (1181-1181)
  • stealth_outputs (1182-1182)
  • stealth_outputs (1194-1194)
  • stealth_outputs (1195-1195)
  • stealth_outputs (1214-1214)
  • stealth_outputs (1215-1215)
  • accounts (590-594)
  • accounts (632-635)
  • accounts (897-900)
  • accounts (960-963)
  • accounts (1059-1062)
crates/wallet/storage_sqlite/src/reader.rs (15)
  • stealth_outputs (838-845)
  • stealth_outputs (868-878)
  • stealth_outputs (891-893)
  • stealth_outputs (927-930)
  • accounts (380-382)
  • accounts (402-405)
  • accounts (418-420)
  • accounts (435-437)
  • accounts (446-448)
  • accounts (477-479)
  • accounts (498-501)
  • accounts (528-531)
  • accounts (563-566)
  • accounts (597-600)
  • utxo_process_queue (1286-1291)
crates/wallet/crypto/tests/viewable_balance_proof.rs (2)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/p2p/src/conversions/transaction.rs (1)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/wallet/crypto/src/confidential.rs (2)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/template_test_tooling/src/support/confidential.rs (4)
bindings/src/types/ConfidentialOutputStatement.ts (1)
  • ConfidentialOutputStatement (10-32)
bindings/src/types/ConfidentialWithdrawProof.ts (1)
  • ConfidentialWithdrawProof (19-30)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/template_lib_types/src/serde_helpers.rs (3)
crates/template_lib_types/src/encrypted_data.rs (2)
  • len (33-35)
  • is_empty (37-39)
crates/template_lib_types/src/memo.rs (2)
  • len (60-65)
  • is_empty (67-69)
crates/template_lib/src/args/freeze_flags.rs (1)
  • is_empty (20-22)
crates/wallet/sdk/src/models/confidential_output.rs (3)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
applications/tari_wallet_cli/src/command/transaction.rs (3)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/template_lib_types/src/memo.rs (1)
  • new_message (41-45)
bindings/src/types/wallet-daemon-client/UtxoInfo.ts (2)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/sdk/src/apis/stealth_crypto.rs (4)
crates/wallet/crypto/src/encrypted_data.rs (2)
  • encrypt_data (54-63)
  • unblind_output (30-52)
crates/wallet/sdk/src/apis/confidential_crypto.rs (1)
  • unblind_output (104-120)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/sdk/src/apis/confidential_outputs.rs (1)
crates/wallet/crypto/src/unblinded_statement.rs (2)
  • value (59-61)
  • memo (67-69)
crates/wallet/storage_sqlite/src/models/stealth_output.rs (5)
bindings/src/types/ComponentAddress.ts (1)
  • ComponentAddress (6-6)
bindings/src/types/RistrettoPublicKeyBytes.ts (1)
  • RistrettoPublicKeyBytes (6-6)
bindings/src/types/UtxoTag.ts (1)
  • UtxoTag (8-8)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/wallet/storage_sqlite/src/models/transaction.rs (1)
  • deserialize_json (46-46)
bindings/src/types/wallet-daemon-client/StealthTransferRequest.ts (2)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/crypto/src/encrypted_data.rs (4)
crates/wallet/sdk/src/apis/confidential_crypto.rs (2)
  • unblind_output (104-120)
  • new (25-27)
crates/wallet/crypto/src/kdfs.rs (1)
  • encrypted_data_dh_kdf_aead (32-44)
crates/wallet/crypto/src/unblinded_statement.rs (4)
  • value (59-61)
  • mask (63-65)
  • memo (67-69)
  • new (39-41)
crates/template_lib_types/src/encrypted_data.rs (4)
  • max_size (29-31)
  • min_size (25-27)
  • len (33-35)
  • as_bytes (41-43)
crates/engine_types/src/crypto/output.rs (2)
bindings/src/types/RistrettoPublicKeyBytes.ts (1)
  • RistrettoPublicKeyBytes (6-6)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/wallet/sdk/src/apis/confidential_transfer.rs (3)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (67-69)
applications/tari_walletd/web_ui/src/components/Memo.tsx (2)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (67-69)
applications/tari_walletd/web_ui/src/routes/StealthUtxoList/StealthUtxoList.tsx (2)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
applications/tari_swarm_daemon/src/process_manager/processes/minotari_wallet.rs (5)
bindings/src/types/PedersenCommitmentBytes.ts (1)
  • PedersenCommitmentBytes (6-6)
bindings/src/types/RistrettoPublicKeyBytes.ts (1)
  • RistrettoPublicKeyBytes (6-6)
bindings/src/types/Scalar32Bytes.ts (1)
  • Scalar32Bytes (3-3)
bindings/src/types/SchnorrSignatureBytes.ts (1)
  • SchnorrSignatureBytes (5-5)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
applications/tari_walletd/src/handlers/accounts.rs (2)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/template_lib_types/src/memo.rs (1)
  • new_message (41-45)
crates/template_lib_types/src/encrypted_data.rs (1)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/wallet/sdk/src/apis/stealth_transfer.rs (3)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (67-69)
crates/wallet/crypto/src/stealth.rs (3)
bindings/src/types/UtxoTag.ts (1)
  • UtxoTag (8-8)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/template_lib_types/src/memo.rs (6)
crates/wallet/crypto/src/unblinded_statement.rs (2)
  • new (39-41)
  • memo (67-69)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/template_lib_types/src/max_bytes.rs (1)
  • new_checked (26-33)
crates/template_lib_types/src/max_string.rs (1)
  • new_checked (20-27)
crates/template_lib_types/src/encrypted_data.rs (5)
  • as_bytes (41-43)
  • len (33-35)
  • is_empty (37-39)
  • max_size (29-31)
  • min_size (25-27)
crates/wallet/crypto/tests/output_statement.rs (4)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
bindings/src/types/UtxoTag.ts (1)
  • UtxoTag (8-8)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/template_lib_types/src/encrypted_data.rs (1)
  • min_size (25-27)
crates/wallet/sdk/tests/confidential_output_api.rs (1)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/template_lib_types/src/max_bytes.rs (2)
crates/template_lib_types/src/hex.rs (2)
  • bytes_from_hex (20-31)
  • bytes_to_hex (33-37)
crates/template_lib_types/src/serde_helpers.rs (6)
  • as_ref (56-61)
  • serialize (100-107)
  • serialize (150-157)
  • deserialize (109-143)
  • deserialize (159-182)
  • len (19-24)
crates/engine_types/src/confidential/claim.rs (1)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/template_lib_types/src/lib.rs (1)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (67-69)
crates/wallet/sdk/tests/crypto_api.rs (4)
crates/wallet/crypto/src/unblinded_statement.rs (3)
  • memo (67-69)
  • mask (63-65)
  • value (59-61)
crates/template_lib_types/src/memo.rs (2)
  • new_message (41-45)
  • as_bytes (53-58)
crates/engine_types/src/crypto/helpers.rs (1)
  • get_commitment_factory (50-52)
crates/template_lib_types/src/encrypted_data.rs (1)
  • as_bytes (41-43)
crates/wallet/sdk/src/apis/stealth_outputs.rs (1)
crates/wallet/crypto/src/unblinded_statement.rs (2)
  • value (59-61)
  • memo (67-69)
crates/template_lib_types/src/max_string.rs (1)
crates/template_lib_types/src/max_bytes.rs (11)
  • deref (20-22)
  • new_checked (26-33)
  • as_ref (41-43)
  • deref_mut (47-50)
  • serialize (76-84)
  • deserialize (88-111)
  • it_returns_some_if_data_le_size (122-127)
  • it_returns_none_if_data_gt_size (130-134)
  • tari_bor (171-171)
  • it_fails_to_deserialize_if_length_is_too_large (162-173)
  • serde_json (164-164)
clients/wallet_daemon_client/src/types.rs (3)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/template_test_tooling/src/support/stealth.rs (3)
bindings/src/types/StealthOutputsStatement.ts (1)
  • StealthOutputsStatement (9-24)
bindings/src/types/StealthTransferStatement.ts (1)
  • StealthTransferStatement (5-13)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/wallet/sdk/src/models/stealth_output.rs (3)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/storage_sqlite/src/writer.rs (2)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/wallet/storage_sqlite/src/reader.rs (4)
  • confidential_outputs (687-691)
  • confidential_outputs (704-706)
  • confidential_outputs (756-766)
  • confidential_outputs (804-807)
crates/wallet/sdk/src/apis/confidential_crypto.rs (2)
crates/wallet/crypto/src/encrypted_data.rs (3)
  • decrypt_data (65-79)
  • encrypt_data (54-63)
  • unblind_output (30-52)
crates/wallet/crypto/src/kdfs.rs (1)
  • encrypted_data_dh_kdf_aead (32-44)
🪛 Biome (2.1.2)
applications/tari_walletd/web_ui/src/components/Memo.tsx

[error] 9-9: Shouldn't redeclare 'Memo'. Consider to delete it or rename it.

'Memo' is defined here:

(lint/suspicious/noRedeclare)

🔇 Additional comments (95)
applications/tari_swarm_daemon/src/process_manager/processes/minotari_wallet.rs (1)

11-15: LGTM! Clean import reorganization.

The import consolidation is correct and improves readability. The relocation of EncryptedData from template_lib_models to tari_template_lib_types aligns with the PR's broader refactoring to support memo functionality.

Cargo.toml (1)

239-239: Confirm the thiserror 2.x upgrade impact.

Jumping from thiserror 1.0.59 to 2.0.17 is a major release; it likely raises the MSRV and tweaks derive macro behaviors. Please confirm our supported toolchain still builds and no crates rely on pre‑2.0 semantics (e.g., transparent/source handling). A quick cargo tree -d thiserror plus a cargo check across the workspace on the target toolchain would help ensure we’re not missing follow‑up changes.

crates/wallet/storage_sqlite/src/models/confidential_output.rs (2)

5-8: LGTM: Import path reorganization is consistent.

The move of EncryptedData from models to types module aligns with the broader refactoring visible across the codebase.


26-26: LGTM: Memo field addition and deserialization follow established patterns.

The memo_json field and its deserialization logic follow the same pattern as other optional fields in this file (e.g., owner_key_id on lines 72-73). The use of as_ref().map(deserialize_json).transpose()? correctly handles the Option-Result composition.

Also applies to: 89-89

crates/wallet/storage_sqlite/src/schema.rs (2)

53-53: LGTM: Schema definitions match migration.

The memo_json column definitions correctly reflect the migration changes as nullable text fields in both confidential_outputs and stealth_outputs tables.

Also applies to: 163-163


259-260: LGTM: Joinable declarations are correct.

The new joinable relationships properly link:

  • stealth_outputs.owner_account_idaccounts.id
  • utxo_process_queue.account_idaccounts.id

Both foreign key relationships are valid and align with the table schemas defined earlier in the file.

crates/wallet/storage_sqlite/src/models/stealth_output.rs (2)

6-12: LGTM: Import reorganization is consistent.

The import path changes align with the broader refactoring moving EncryptedData to the types module, matching the changes in confidential_output.rs.


37-37: LGTM: Memo field handling mirrors confidential outputs.

The memo_json field addition and deserialization logic are identical to the pattern used in confidential_output.rs, ensuring consistency across output types.

Also applies to: 83-83

crates/wallet/storage_sqlite/src/writer.rs (3)

48-54: LGTM: Import path change is consistent.

The move of EncryptedData to the types module matches the changes in the model files and maintains consistency across the storage layer.


951-951: LGTM: Memo deserialization in lock path is correct.

The deserialization logic follows the same pattern used in the model conversion methods, ensuring consistency across all read paths.


983-983: LGTM: Memo serialization is symmetric and consistent.

Both confidential and stealth output insert paths correctly serialize the memo field to memo_json using the same pattern. The serialization is the inverse of the deserialization operations in the read paths, ensuring data integrity.

Also applies to: 1127-1127

crates/p2p/src/conversions/transaction.rs (1)

47-47: LGTM! Import path update aligns with type reorganization.

The relocation of EncryptedData to tari_template_lib::types is consistent with the broader refactoring to centralize crypto data types in tari_template_lib_types.

applications/tari_walletd/src/handlers/accounts.rs (6)

37-37: LGTM! Memo type import supports new memo functionality.

The import is correctly added to the existing use tari_template_lib::types statement.


458-469: Verify the skip_memo flag is intentionally set to true.

The skip_memo parameter is hardcoded to true when decrypting the claim burn output. This means any memo from the L1 burn transaction will be ignored. If this is intentional (e.g., L1 burns don't include memos or they're not needed here), this is fine. Otherwise, consider whether the memo should be preserved and potentially logged or stored.


494-500: LGTM! Passing None for memo maintains L1 burn format compatibility.

The encryption here creates an output with the same encrypted data format used on L1, so omitting the memo is appropriate.


913-913: LGTM! Memo correctly propagated through confidential transfer flow.

The memo from the request is properly passed through to the ConfidentialTransferParams.


956-956: LGTM! Output memo correctly propagated through stealth transfer flow.

The output memo from the request is properly passed through to the StealthTransferParams.


521-521: Remove outdated check; Memo::new_message is implemented and available.

crates/wallet/sdk/src/apis/confidential_crypto.rs (4)

8-8: LGTM! Imports support the refactored encryption/decryption API.

The new imports (DecryptedData, Memo, and the updated encrypted_data functions) are necessary for the memo feature and the unified decryption return type.

Also applies to: 11-11, 19-19


66-77: LGTM! Memo parameter correctly added to encryption API.

The method signature cleanly extends to accept an optional memo, and properly delegates to the underlying encrypt_data function with the memo parameter.


79-88: LGTM! Decryption API refactored to support memos.

The method rename from extract_value_and_mask to decrypt_output_data improves clarity, and the new return type DecryptedData unifies the mask/value and optional memo into a single structure. The skip_memo parameter provides flexibility for callers who don't need memo data.


104-120: LGTM! Unblinding API updated consistently with decryption changes.

The addition of the skip_memo parameter and the DecryptedData return type maintain consistency with the other decryption methods in this API.

clients/wallet_daemon_client/src/types.rs (5)

57-57: LGTM! Import additions support memo functionality.

The imports of ConfidentialOutputStatement and Memo are necessary for the new memo fields in the request/response types.

Also applies to: 59-59


494-495: LGTM! Optional memo field added with proper serialization attributes.

The memo field is correctly marked as optional with #[serde(default, skip_serializing_if = "Option::is_none")], ensuring backward compatibility with existing clients that don't provide a memo.


549-550: LGTM! Consistent memo field addition to ConfidentialTransferRequest.

The optional memo field follows the same pattern as ProofsGenerateRequest, maintaining API consistency.


1067-1068: LGTM! output_memo field clearly distinguishes memo purpose.

The field is appropriately named output_memo to clarify that this memo is attached to the transfer output, which is clearer than a generic memo field for stealth transfers.


1109-1109: LGTM! Memo field added to UTXO information.

The optional memo field allows the API to expose memo data associated with UTXOs when available.

integration_tests/tests/steps/wallet.rs (1)

17-17: LGTM! EncryptedData import path updated consistently.

The import path change from models::EncryptedData to types::EncryptedData aligns with the type reorganization across the codebase.

crates/wallet/sdk/src/models/stealth_output.rs (2)

5-7: LGTM: Import path updates correctly reflect the type reorganization.

The import changes align with the project-wide migration of EncryptedData and introduction of Memo in tari_template_lib::types.


25-25: LGTM: Optional memo field added to support UTXO memos.

The Option<Memo> field correctly supports the new optional memo feature without breaking existing code.

crates/wallet/crypto/src/stealth.rs (2)

164-164: LGTM: Import path updated for consistency.

The import of EncryptedData from tari_template_lib::types aligns with the project-wide type reorganization.


180-180: LGTM: Test data updated for new memo field.

The test correctly initializes the memo field as None, maintaining test validity with the updated UnblindedOutputStatement structure.

bindings/src/index.ts (1)

66-66: LGTM: Memo type properly exported.

The export makes the Memo type publicly available and is correctly positioned in the alphabetical export list.

crates/wallet/crypto/src/confidential.rs (2)

143-143: LGTM: Import paths updated for type reorganization.

The import of Amount and EncryptedData from tari_template_lib::types aligns with the project-wide refactoring.


157-157: LGTM: Test data updated for memo field.

The test correctly initializes the memo field as None to maintain test validity with the updated structure.

crates/engine_types/src/confidential/claim.rs (1)

10-10: LGTM: Import path updated for EncryptedData.

The import path change from tari_template_lib::models to tari_template_lib::types is consistent with the project-wide type reorganization.

crates/wallet/crypto/tests/viewable_balance_proof.rs (2)

15-15: LGTM: Import paths updated for type reorganization.

The import of Amount and EncryptedData from tari_template_lib::types aligns with the project-wide refactoring.


28-28: LGTM: Test helper updated for memo field.

The test helper function correctly initializes the memo field as None, maintaining test validity with the updated UnblindedOutputStatement structure.

applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/steps/FormStep.tsx (2)

44-44: LGTM!

The memo field addition to the SendMoneyFormState interface is straightforward and correctly typed.


178-188: Verify UTF-8 byte length constraint.

The maxLength: 253 attribute limits JavaScript string length (UTF-16 code units), not UTF-8 byte length. Multi-byte characters (e.g., emoji, certain Unicode) can exceed 253 bytes when UTF-8 encoded, even if the string length is under 253.

Consider adding UTF-8 byte length validation:

 {transferFormState.outputToConfidential ? (
   <TextField
     name="memo"
     label="Memo message (optional, max 253 characters)"
-    inputProps={{ maxLength: 253 }}
     value={transferFormState.memo}
     onChange={onFormValueChange}
     style={{ flexGrow: 1 }}
     disabled={disabled}
+    error={new TextEncoder().encode(transferFormState.memo).length > 253}
+    helperText={
+      new TextEncoder().encode(transferFormState.memo).length > 253
+        ? "Memo exceeds 253 bytes when UTF-8 encoded"
+        : undefined
+    }
   />
 ) : null}
bindings/src/types/Memo.ts (1)

1-3: LGTM!

The Memo type definition is correct and properly structured as a union type matching the Rust enum pattern. The generated file warning is appropriately included.

crates/wallet/sdk/tests/support/harness.rs (2)

28-28: LGTM!

The import path update for EncryptedData from tari_template_lib::models to tari_template_lib::types is correct and aligns with the broader refactoring mentioned in the PR summary.


110-110: LGTM!

The addition of memo: None to the test harness is appropriate, as test outputs don't require memos.

applications/tari_walletd/web_ui/src/services/api/hooks/useAccounts.ts (3)

49-49: LGTM!

The Memo import is correctly added to support the new output_memo field.


129-129: LGTM!

The optional output_memo field addition to TransferParams is correctly typed.


148-148: LGTM!

The output_memo field is correctly propagated to both confidential and stealth transfer requests, with the || null pattern appropriately converting undefined to null for optional fields.

Also applies to: 161-161

crates/template_lib/src/prelude.rs (1)

41-42: LGTM!

The public re-exports of MaxBytes and MaxString appropriately expand the prelude's public API surface. These bounded data types align well with the memo functionality being introduced.

integration_tests/src/wallet_daemon_client.rs (2)

129-129: LGTM!

The output_memo: None initialization is appropriate for integration tests where memos are not being tested.


885-885: LGTM!

The memo: None initialization is appropriate for integration tests where memos are not being tested.

applications/tari_walletd/src/handlers/stealth_utxos.rs (1)

49-49: LGTM!

The memo field mapping to UtxoInfo is correctly implemented, consistent with other field mappings in the response.

bindings/src/types/wallet-daemon-client/StealthTransferRequest.ts (2)

4-4: LGTM!

The Memo import is correctly added to support the new optional field.


17-17: LGTM!

The output_memo field is correctly typed as optional and nullable, consistent with the pattern used for other optional fields in the request.

bindings/src/types/wallet-daemon-client/ConfidentialTransferRequest.ts (1)

4-18: LGTM!

The addition of the optional memo field is consistent with the Memo type definition and follows the existing nullable pattern used in other fields.

crates/template_test_tooling/src/support/confidential.rs (5)

14-14: LGTM!

The import path change for EncryptedData from models to types aligns with the broader refactoring to consolidate types in the tari_template_lib::types module.


38-46: LGTM!

The addition of memo: None to UnblindedOutputStatement is consistent with the new memo field support across the codebase.


49-57: LGTM!

Consistent memo field initialization.


154-162: LGTM!

Consistent memo field initialization.


163-171: LGTM!

Consistent memo field initialization.

applications/tari_walletd/web_ui/src/routes/AssetVault/Tokens/components/SendMoney.tsx (2)

193-193: LGTM!

The conditional memo inclusion using { Message: transferFormState.memo } is consistent with the Memo type shape and follows the same pattern used in the actual transfer path.


257-257: LGTM!

Consistent memo handling in the actual transfer path, matching the fee estimation logic.

crates/wallet/crypto/tests/output_statement.rs (2)

19-19: LGTM!

The import path simplification removes the top-level EncryptedData import since it's now imported within the stealth_tests module where it's actually used.


31-31: LGTM!

The consolidated import brings both UtxoTag and EncryptedData from the types module, aligning with the broader reorganization of these types.

applications/tari_walletd/web_ui/src/routes/StealthUtxoList/StealthUtxoList.tsx (3)

34-34: LGTM!

Import of the Memo component for rendering memo values.


66-97: LGTM!

The column width adjustments and the addition of the Memo column header are well-integrated into the existing table structure.


114-116: LGTM!

Consistent usage of the Memo component to render utxo.memo values in the table.

crates/wallet/sdk/tests/confidential_output_api.rs (2)

12-12: LGTM!

The import path change for EncryptedData aligns with its relocation to the types module.


85-99: LGTM!

The addition of memo: None to the ConfidentialOutputModel initialization is consistent with the new optional memo field support.

crates/template_test_tooling/src/support/stealth.rs (3)

21-27: LGTM!

The import reorganization consolidates model types under one import and moves EncryptedData to the types module, aligning with the broader type system refactoring.


90-98: LGTM!

Consistent addition of memo: None to the UnblindedOutputStatement initialization.


178-187: LGTM!

Consistent memo field initialization in the transfer data generation.

crates/wallet/sdk/tests/base_layer_compat.rs (1)

9-18: Clarify MemoField truncation/metadata behavior

The test feeds 254 bytes yet MAX_BYTES_LENGTH is 253 and the assertion slices memo_bytes[1..=253]. Confirm that new_open_from_string prepends a metadata byte and truncates the payload to MAX_BYTES_LENGTH, and add a code comment explaining this behavior.

crates/wallet/sdk/src/apis/confidential_outputs.rs (1)

182-191: Nice reuse of decrypt_output_data

Great to see the mask resolution path using the shared decrypt_output_data call—this keeps memo-aware parsing aligned across the stack.

applications/tari_wallet_cli/src/command/transaction.rs (1)

156-158: CLI memo option wired cleanly

The optional memo flag plumbs through neatly, and the length validation via Memo::new_message keeps it safe for users.

Also applies to: 436-439

crates/wallet/sdk/src/apis/stealth_outputs.rs (2)

226-233: LGTM! Skip memo optimization for spending.

The logic correctly skips memo decryption during spending operations since only the mask and value are needed. The comment clearly explains the rationale, and the API usage aligns with the new DecryptedData return type.


519-596: LGTM! Memo propagation in UTXO validation.

The validation flow correctly:

  • Decrypts memos when validating UTXOs (skip_memo=false)
  • Consistently extracts and propagates memo across all status branches (Unspent/Invalid)
  • Includes memo in the final StealthOutputModel construction

The implementation maintains existing ownership validation logic while seamlessly adding memo support.

applications/tari_walletd/src/handlers/confidential.rs (2)

100-125: LGTM! Memo support in transfer proofs.

The implementation correctly threads the memo from the request through encryption and into the UnblindedOutputStatement. The API usage aligns with the updated signatures.


150-183: LGTM! Change output memo handling.

Change outputs correctly use None for memos at all stages (encryption, model, statement). This is the expected behavior since change returns to the sender and doesn't require a memo.

crates/wallet/sdk/src/apis/stealth_crypto.rs (3)

13-27: LGTM! Import updates for memo support.

The import changes correctly bring in the new types (DecryptedData, Memo, EncryptedData) and updated function signatures needed for memo support. The imports are well-organized and align with the API changes.


102-113: LGTM! Encryption API extended for memo support.

The encrypt_value_and_mask method correctly extends its signature to accept an optional memo and delegates to encrypt_data. The API design is clean and maintains backward compatibility through the Option type.


129-145: LGTM! Decryption API updated for memo support.

The decrypt_value_and_mask method correctly:

  • Adds the skip_memo parameter to enable optimization
  • Changes return type to DecryptedData to include memo information
  • Delegates to unblind_output with proper parameter passing

The API maintains consistency with the broader memo support implementation.

crates/wallet/crypto/src/encrypted_data.rs (6)

30-52: LGTM! Unblinding updated with memo support.

The unblind_output function correctly:

  • Extends the API with skip_memo parameter and DecryptedData return type
  • Preserves critical commitment validation logic
  • Maintains proper error handling with CommitmentMismatchDecryptedData

The security-critical commitment check remains intact.


54-63: LGTM! Encryption API with memo support.

The encrypt_data function cleanly extends the encryption API to accept an optional memo and delegates to the inner implementation. The commitment is computed before encryption, maintaining the correct order of operations.


65-79: LGTM! Decryption API with memo support.

The decrypt_data function correctly extends the decryption API with skip_memo parameter and constructs the DecryptedData result from the decrypted components.


166-226: LGTM! Decryption with memo support.

The decrypt_inner function correctly:

  • Extracts and validates tag, nonce, and payload with proper error handling
  • Decodes value and mask as before
  • Conditionally decodes memo based on skip_memo flag and memo presence
  • Handles empty memo bytes correctly
  • Maps memo decoding errors appropriately

The implementation is thorough and handles edge cases well.


228-304: LGTM! Comprehensive test coverage.

The test suite covers:

  • Basic encryption/decryption without memo
  • Various memo types (message, bytes, empty, maximum length)
  • skip_memo functionality
  • Round-trip encoding/decoding

The tests provide good coverage of the new memo functionality and edge cases.


126-158: EncryptedData::try_from enforces both min_size() and max_size() bounds, ensuring any payload length between those values—including variable-length memo encodings—is accepted and rejected outside that range.

crates/template_lib_types/src/max_string.rs (4)

6-17: LGTM! Well-designed bounded string type.

The MaxString type uses:

  • Generic const parameter for compile-time length bounds
  • Box<str> for efficient storage without extra capacity
  • Deref to enable transparent string operations

The design is clean and memory-efficient.


19-32: LGTM! Safe construction with validation.

The new_checked constructor properly validates the length constraint and returns Option to handle validation failures. The into_string method provides a way to convert back to String. No unsafe constructor is exposed, preventing invariant violations.


47-62: LGTM! Serde implementation with validation.

The serde implementation correctly:

  • Serializes as a plain string
  • Validates length during deserialization using new_checked
  • Provides clear error messages including the actual length

The pattern is consistent with the MaxBytes implementation shown in the snippets.


64-110: LGTM! Comprehensive test coverage.

The test suite covers:

  • Valid length construction
  • Length overflow rejection
  • Serde round-trip with tari_bor
  • Deserialization failures with both JSON and binary formats
  • Error message verification

The tests provide good coverage and follow patterns consistent with MaxBytes.

crates/template_lib_types/src/encrypted_data.rs (1)

62-66: Serde/TS shape: confirm MaxBytes serializes to a string

You removed serde(dynamic_hex) and switched to MaxBytes<…> with ts(type = "string"), while bindings map EncryptedData to string (bindings/src/types/EncryptedData.ts). Ensure MaxBytes serializes/deserializes as a string (hex/base64) and not as a JSON array. If not, UI/SDK bindings will break.

Please confirm MaxBytes serde impl returns a string and specify which encoding (hex/base64). If it’s not a string, either:

  • add serde(with = "...") to enforce string encoding, or
  • update bindings/UI accordingly.

Based on learnings

Also applies to: 71-81

crates/template_lib_types/src/memo.rs (2)

27-35: Public API looks solid; TS/serde alignment okay

Memo enum with MaxString/MaxBytes capped at 253 bytes is clear, derives serde and ts union matches bindings. Good introduction.


53-69: Double-check serde representation for Bytes in bindings/UI

Bytes variant maps to string in TS. Ensure serde for MaxBytes encodes to the same string format the UI expects. The web UI does Buffer.from(memo.Bytes).toString("hex") without specifying the input encoding.

  • If serde emits base64: UI should use Buffer.from(memo.Bytes, "base64").
  • If serde emits hex: UI is fine.
    Please confirm and align one way across server and UI.

Based on learnings

crates/wallet/sdk/src/apis/confidential_transfer.rs (1)

16-17: Memo plumbing looks correct; ensure encryption API and storage match

  • Passing params.memo.clone() into create_confidential_proof_statement and forwarding memo.as_ref() to encrypt_value_and_mask is correct.
  • Persisting memo: None for change outputs is sensible.

Please verify:

  • encrypt_value_and_mask accepts Option<&Memo> and includes the memo into EncryptedData as per the new format.
  • DB/storage layer schema for ConfidentialOutputModel includes memo and matches serde/TS representation.

Also applies to: 321-323, 433-434, 457-458, 467-468, 495-499

crates/wallet/sdk/src/apis/stealth_transfer.rs (1)

33-34: End-to-end memo support in stealth path is consistent

  • Threading output_memo through create_output_statement and storing memo is consistent.
  • Fee/change outputs use None as expected.

Please confirm:

  • Stealth encrypt_value_and_mask handles Option<&Memo> identically to confidential path.
  • StealthOutputModel schema and migrations include memo (nullable) and serialization shape is consistent with bindings/UI.

Also applies to: 459-460, 477-479, 749-755, 788-789, 804-805, 828-829, 845-846, 876-878

Comment thread applications/tari_walletd/web_ui/src/components/Memo.tsx
Comment thread crates/engine_types/src/crypto/output.rs
Comment thread crates/template_lib_types/src/encrypted_data.rs Outdated
Comment on lines +40 to +45
impl<const N: usize> DerefMut for MaxString<N> {
fn deref_mut(&mut self) -> &mut Self::Target {
// Mutable but not resizeable
&mut self.s
}
}

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 | 🟠 Major

🧩 Analysis chain

Verify DerefMut safety for bounded string.

The DerefMut implementation returns &mut str, which allows in-place mutation. While the comment states "Mutable but not resizeable", certain operations on &mut str could potentially violate the length invariant.

Consider the following:

  • &mut str allows methods like make_ascii_uppercase() which modify bytes in place (safe)
  • However, user code with &mut str could use unsafe operations or create confusing APIs

Verify that this design is intentional and document any safety considerations. Check if similar bounded types in the codebase use DerefMut:


🏁 Script executed:

#!/bin/bash
# Search for similar patterns in MaxBytes and other bounded types
ast-grep --pattern $'impl$_DerefMut for $_ {
  fn deref_mut(&mut self) -> &mut $_ {
    $$$
  }
}'

# Check if MaxBytes has similar DerefMut
rg -n 'impl.*DerefMut.*MaxBytes' --type rust

Length of output: 93


Document DerefMut safety for MaxString

The DerefMut impl on MaxString mirrors MaxBytes, returning &mut str which enforces only length-preserving, in-place edits. Add a doc-comment clarifying that resizing isn’t supported and only safe, length-preserving mutations are allowed.

🤖 Prompt for AI Agents
In crates/template_lib_types/src/max_string.rs around lines 40 to 45, the
DerefMut impl lacks documentation about safety constraints; add a doc-comment
above the impl explaining that the returned &mut str permits only in-place,
length-preserving mutations (no resizing, truncation, or growth) and that
callers must not change the string length because MaxString enforces a fixed
capacity—mirror the wording from MaxBytes to clarify the safety contract.

@github-actions

Copy link
Copy Markdown

Test Results (CI)

136 tests   - 291   135 ✅  - 292   25m 6s ⏱️ - 26m 12s
 18 suites  -  44     0 💤 ±  0 
  1 files   ±  0     1 ❌ +  1 

For more details on these failures, see this check.

Results for commit f6a2dee. ± Comparison against base commit 70f1f28.

This pull request removes 291 tests.
tari_engine::limits ‑ practical_limit
tari_engine::no_std ‑ it_compiles_when_using_no_std
tari_engine::publish_template ‑ publish_template_invalid_binary
tari_engine::publish_template ‑ publish_template_success
tari_engine::publish_template ‑ publish_template_too_big_binary
tari_engine::recall ‑ it_recalls_all_resource_types
tari_engine::reentrancy ‑ it_allows_multiple_immutable_access_to_component
tari_engine::reentrancy ‑ it_prevents_multiple_mutable_access_to_component
tari_engine::reentrancy ‑ it_prevents_read_access_to_mutating_component
tari_engine::reentrancy ‑ it_prevents_reentrant_withdraw
…

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🧹 Nitpick comments (8)
crates/wallet/crypto/src/encrypted_data.rs (2)

173-195: Consider improving error context for malformed encrypted data.

The error handling for extracting tag, nonce, and payload slices uses generic error messages. If the encrypted data is malformed (e.g., too short), the errors don't indicate which specific field failed to parse.

Consider adding more specific error context:

 let tag = Tag::from_slice(
     encrypted_data
         .tag_slice()
         .ok_or_else(|| WalletCryptoError::FailedDecryptData {
-            details: "Failed to get tag slice".to_string(),
+            details: format!("EncryptedData too short for tag (length: {})", encrypted_data.len()),
         })?,
 );
 let nonce =
     XNonce::from_slice(
         encrypted_data
             .nonce_slice()
             .ok_or_else(|| WalletCryptoError::FailedDecryptData {
-                details: "Failed to get nonce slice".to_string(),
+                details: format!("EncryptedData too short for nonce (length: {})", encrypted_data.len()),
             })?,
     );
 let mut bytes = Zeroizing::new(
     encrypted_data
         .payload_slice()
         .ok_or_else(|| WalletCryptoError::FailedDecryptData {
-            details: "Failed to get payload slice".to_string(),
+            details: format!("EncryptedData too short for payload (length: {})", encrypted_data.len()),
         })?
         .to_vec(),
 );

This aids debugging when malformed data is encountered.


216-224: Validate trailing bytes after memo decode
Memo::decode_from only consumes the tag, length, and payload bytes—leaving extra bytes in the reader, which are then silently dropped by the wrapper in encrypted_data.rs (lines 216–224). To avoid hidden data loss, consider:

  • Asserting memo_bytes.is_empty() post‐decode and returning an error if not.
  • Or preserving leftover bytes for future memo extensions.
crates/wallet/sdk/src/apis/stealth_transfer.rs (1)

799-861: Align UnblindedOutputStatement initializer fields to match its declaration order.
encrypted_data and minimum_value_promise are swapped; reordering them to mirror the struct’s field sequence improves readability.

crates/wallet/sdk/src/apis/stealth_crypto.rs (1)

130-146: Naming nit: consider aligning with “decrypt_output_data”/“unblind_output”

This method performs unblinding and returns DecryptedData. Consider renaming to unblind_output or decrypt_output_data for parity with ConfidentialCryptoApi.

crates/wallet/crypto/src/memo.rs (1)

160-239: Tests are comprehensive

Round-trips, bounds, unknown-tag fallback, and size ceilings are covered. Minor typo in comment (“THis”), but not blocking.

crates/wallet/sdk/src/apis/confidential_crypto.rs (1)

105-121: Unblind with skip_memo: OK. Consider naming consistency across modules

This mirrors the stealth API; consider standardizing method names across Confidential/Stealth for easier SDK consumption.

crates/wallet/crypto/Cargo.toml (1)

17-27: Argon2 usage confirmed; align subtle with workspace

  • argon2 is required in crates/wallet/crypto/src/encryption.rs.
  • subtle is used; if it’s declared in the workspace, switch to workspace = true here for consistency.
applications/tari_walletd/src/handlers/accounts.rs (1)

459-465: Avoid magic boolean in decrypt call; name the intent

The trailing true is opaque. Prefer an inline named comment or local named var for clarity.

-        true,
+        /* verify_commitment */ true,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f6a2dee and b85c7a2.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • applications/tari_wallet_cli/src/command/transaction.rs (4 hunks)
  • applications/tari_walletd/src/handlers/accounts.rs (7 hunks)
  • bindings/src/types/wallet-daemon-client/UtxoInfo.ts (1 hunks)
  • clients/wallet_daemon_client/src/types.rs (6 hunks)
  • crates/template_abi/src/rust.rs (0 hunks)
  • crates/template_lib/src/prelude.rs (1 hunks)
  • crates/template_lib_types/src/lib.rs (1 hunks)
  • crates/wallet/crypto/Cargo.toml (1 hunks)
  • crates/wallet/crypto/src/encrypted_data.rs (6 hunks)
  • crates/wallet/crypto/src/lib.rs (1 hunks)
  • crates/wallet/crypto/src/memo.rs (1 hunks)
  • crates/wallet/crypto/src/unblinded_statement.rs (3 hunks)
  • crates/wallet/sdk/Cargo.toml (1 hunks)
  • crates/wallet/sdk/src/apis/confidential_crypto.rs (3 hunks)
  • crates/wallet/sdk/src/apis/confidential_transfer.rs (8 hunks)
  • crates/wallet/sdk/src/apis/stealth_crypto.rs (3 hunks)
  • crates/wallet/sdk/src/apis/stealth_transfer.rs (9 hunks)
  • crates/wallet/sdk/src/models/confidential_output.rs (2 hunks)
  • crates/wallet/sdk/src/models/stealth_output.rs (2 hunks)
  • crates/wallet/sdk/tests/base_layer_compat.rs (1 hunks)
  • crates/wallet/sdk/tests/crypto_api.rs (2 hunks)
💤 Files with no reviewable changes (1)
  • crates/template_abi/src/rust.rs
🚧 Files skipped from review as they are similar to previous changes (5)
  • crates/template_lib_types/src/lib.rs
  • bindings/src/types/wallet-daemon-client/UtxoInfo.ts
  • crates/wallet/sdk/src/apis/confidential_transfer.rs
  • applications/tari_wallet_cli/src/command/transaction.rs
  • clients/wallet_daemon_client/src/types.rs
🧰 Additional context used
🧬 Code graph analysis (12)
crates/wallet/sdk/tests/base_layer_compat.rs (2)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (69-71)
crates/wallet/crypto/src/memo.rs (2)
  • decode_from (101-133)
  • new_bytes (48-52)
crates/wallet/crypto/src/unblinded_statement.rs (3)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/sdk/src/apis/stealth_transfer.rs (1)
  • value (950-952)
applications/tari_walletd/src/handlers/accounts.rs (4)
crates/wallet/crypto/src/unblinded_statement.rs (2)
  • memo (69-71)
  • mask (65-67)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/crypto/src/memo.rs (1)
  • new_message (42-46)
crates/wallet/sdk/src/models/stealth_output.rs (3)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (69-71)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/crypto/src/lib.rs (1)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (69-71)
crates/wallet/sdk/src/apis/stealth_crypto.rs (3)
crates/wallet/crypto/src/encrypted_data.rs (2)
  • encrypt_data (54-63)
  • unblind_output (30-52)
crates/wallet/sdk/src/apis/confidential_crypto.rs (1)
  • unblind_output (105-121)
crates/wallet/crypto/src/kdfs.rs (1)
  • encrypted_data_dh_kdf_aead (32-44)
crates/wallet/sdk/tests/crypto_api.rs (2)
crates/wallet/crypto/src/unblinded_statement.rs (3)
  • memo (69-71)
  • mask (65-67)
  • value (61-63)
crates/wallet/crypto/src/memo.rs (2)
  • new_message (42-46)
  • as_bytes (54-59)
crates/wallet/sdk/src/apis/confidential_crypto.rs (3)
crates/wallet/crypto/src/encrypted_data.rs (3)
  • decrypt_data (65-79)
  • encrypt_data (54-63)
  • unblind_output (30-52)
crates/wallet/crypto/src/unblinded_statement.rs (2)
  • memo (69-71)
  • mask (65-67)
crates/wallet/crypto/src/kdfs.rs (1)
  • encrypted_data_dh_kdf_aead (32-44)
crates/wallet/crypto/src/memo.rs (2)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (69-71)
crates/template_lib_types/src/encrypted_data.rs (2)
  • max_size (30-32)
  • min_size (26-28)
crates/wallet/sdk/src/apis/stealth_transfer.rs (1)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (69-71)
crates/wallet/crypto/src/encrypted_data.rs (8)
bindings/src/types/PedersenCommitmentBytes.ts (1)
  • PedersenCommitmentBytes (6-6)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/wallet/crypto/src/unblinded_statement.rs (4)
  • memo (69-71)
  • value (61-63)
  • mask (65-67)
  • new (41-43)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/sdk/src/apis/confidential_crypto.rs (2)
  • unblind_output (105-121)
  • new (26-28)
crates/wallet/crypto/src/kdfs.rs (1)
  • encrypted_data_dh_kdf_aead (32-44)
crates/template_lib_types/src/encrypted_data.rs (4)
  • max_size (30-32)
  • min_size (26-28)
  • len (34-36)
  • as_bytes (42-44)
crates/wallet/crypto/src/memo.rs (5)
  • decode_from (101-133)
  • len (61-66)
  • new_message (42-46)
  • as_bytes (54-59)
  • new_bytes (48-52)
crates/wallet/sdk/src/models/confidential_output.rs (3)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (69-71)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
⏰ 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). (6)
  • GitHub Check: machete
  • GitHub Check: test
  • GitHub Check: check nightly
  • GitHub Check: check stable
  • GitHub Check: clippy
  • GitHub Check: fmt
🔇 Additional comments (28)
crates/template_lib/src/prelude.rs (1)

41-42: LGTM MaxBytes and MaxString are defined in tari_template_lib_types and in use within the memo implementation.

crates/wallet/crypto/src/encrypted_data.rs (3)

30-52: LGTM! Good error handling for commitment mismatch.

The refactored unblind_output correctly:

  • Accepts the new skip_memo parameter to control memo decryption
  • Returns DecryptedData containing both the value/mask and optional memo
  • Validates the commitment matches the decrypted data
  • Provides clear error messages for invariant violations

The commitment verification at lines 47-51 is critical for security and correctly prevents acceptance of invalid decrypted data.


95-164: Review the memo encoding and buffer allocation strategy.

The encrypt_data_inner function allocates the full EncryptedData::max_size() when a memo is present (lines 126-130), even if the memo is smaller than the maximum. This means:

  1. Buffer size: Every encrypted output with a memo will be max_size() bytes, regardless of the actual memo length.
  2. On-chain footprint: This could significantly increase transaction sizes if most memos are shorter than the maximum.
  3. Alternative approach: Consider allocating only the required size: min_size() + actual_memo_encoded_length to reduce on-chain bloat.

The current approach is simpler and avoids variable-length parsing complexity, but comes at the cost of larger payloads. This trade-off should be intentional.

Is the fixed-size allocation for memos intentional? If optimizing for smaller payloads is desired, consider dynamic sizing:

 let mut bytes = vec![
     0;
-    memo.map(|_| EncryptedData::max_size())
+    memo.as_ref().map(|m| {
+        let mut temp = Vec::new();
+        m.encode_into(&mut temp).ok()?;
+        Some(EncryptedData::min_size() + temp.len())
+    }).flatten()
         .unwrap_or(EncryptedData::min_size())
 ];

However, this adds complexity and requires two passes (one to measure, one to encode). The current fixed-size approach may be preferred for simplicity.


236-304: LGTM! Comprehensive test coverage.

The test suite thoroughly covers:

  • Basic encryption/decryption without memo
  • Message memo encryption/decryption
  • Byte memo encryption/decryption
  • Empty memo handling
  • Maximum-length memo handling
  • skip_memo flag behavior

The tests validate both correctness and the edge cases of the memo implementation.

crates/wallet/sdk/src/apis/stealth_transfer.rs (2)

460-461: LGTM! Correct memo propagation for different output types.

The implementation correctly differentiates memo handling:

  • Line 460: Primary transfer output includes the user-provided params.output_memo.clone()
  • Line 479: Fee change output explicitly sets memo to None (appropriate, as this is an internal output)
  • Lines 750-756: Change output explicitly sets memo to None (appropriate, as this returns funds to the sender)

This design ensures memos are only attached to the intended recipient's output, not internal bookkeeping outputs.

Also applies to: 479-479, 750-756


877-878: Good documentation for the memo field.

The documentation clearly explains the purpose of the output_memo field: "Optional memo to include a memo in the output. This memo is encrypted and can only be read by the recipient."

This is helpful for API users understanding the feature.

crates/wallet/crypto/src/lib.rs (1)

16-16: LGTM! Memo module correctly exposed.

The addition of pub mod memo; properly exposes the memo module as part of the public API, consistent with the other public modules in the crate.

crates/wallet/sdk/Cargo.toml (1)

16-16: LGTM! Serde feature appropriately enabled.

Adding the serde feature for tari_ootle_wallet_crypto is necessary to support serialization/deserialization of the Memo type in the SDK models and APIs.

crates/wallet/sdk/src/models/confidential_output.rs (2)

26-26: LGTM! Memo field added to model.

The addition of pub memo: Option<Memo> to ConfidentialOutputModel is consistent with the stealth output model and allows storing memo data for confidential outputs.


6-11: Approve code changes
No remaining stale imports of EncryptedData detected after search.

crates/wallet/sdk/src/models/stealth_output.rs (2)

4-9: Consistent import path updates.

The EncryptedData import path change from tari_template_lib::models to tari_template_lib::types is consistent with the refactoring seen in confidential_output.rs.


26-26: LGTM! Memo field added to stealth output model.

The addition of pub memo: Option<Memo> to StealthOutputModel allows storing memo data for stealth outputs, completing the memo support across both output types.

crates/wallet/sdk/tests/crypto_api.rs (1)

84-94: LGTM! Test properly updated for memo support.

The test correctly:

  • Creates a memo using Memo::new_message("Hello, world!")
  • Passes it to the encryption function with Some(&memo)
  • Specifies skip_memo: false during decryption
  • Uses accessor methods (value(), mask(), memo()) instead of direct field access
  • Verifies the memo is correctly decrypted

The use of accessor methods is good practice and provides better encapsulation.

crates/wallet/sdk/tests/base_layer_compat.rs (1)

9-18: Re-validate test payload length and slice
Local Memo::MAX_BYTES_LENGTH = 253, but the test constructs a 254-byte string. The slice memo_bytes[1..=Memo::MAX_BYTES_LENGTH] also includes the length prefix byte, not just the raw payload. Verify the external MemoField::to_bytes() encoding (tag + length) and adjust the test to use ≤253 bytes or drop the length byte.

crates/wallet/crypto/src/unblinded_statement.rs (2)

50-76: DecryptedData API looks solid

Accessors, ownership (into_mask_and_value), and commitment derivation are correct and consistent with MaskAndValue.


18-18: Adding memo to UnblindedOutputStatement

Field addition is straightforward and keeps memo optional; no issues spotted.

crates/wallet/sdk/src/apis/stealth_crypto.rs (1)

103-114: Encrypt with memo: correct KDF and parameter threading

Uses encrypted_data_dh_kdf_aead(secret, public_key) and passes memo to encrypt_data; looks correct.

crates/wallet/crypto/Cargo.toml (1)

33-35: Feature flags look good

Optional serde/ts-rs gating is clean and non-invasive.

crates/wallet/sdk/src/apis/confidential_crypto.rs (2)

67-77: Encrypt with memo: correct usage

KDF and memo threading into encrypt_data look correct.


80-89: Decrypt API parity

Returning DecryptedData and exposing skip_memo is consistent with wallet_crypto; looks good.

crates/wallet/crypto/src/memo.rs (2)

27-36: Bytes variant serializes as hex string in JSONMaxBytes<MAX_BYTES_LENGTH>’s serde::Serialize impl emits a hex string when serializer.is_human_readable(), aligning with the UI binding.


101-113: decode_from already receives a bounded memo reader
In decrypt_inner, we slice the decrypted payload to just the memo bytes and pass that subslice to Memo::decode_from, so it cannot read past the memo segment.

applications/tari_walletd/src/handlers/accounts.rs (6)

20-24: Import looks correct for the type actually used by UnblindedOutputStatement

Using Memo from tari_ootle_wallet_crypto aligns with UnblindedOutputStatement’s Memo. No issues.


469-472: Final amount calc is correct and safe

Uses decrypted.value() with checked_sub_positive; good overflow/underflow handling.


531-534: Input statement creation looks correct

Using decrypted.into_mask_and_value() is appropriate for ownership spend.


904-916: Memo propagation enabled for confidential transfers

Good threading of req.memo. Please ensure boundary tests (<=253 bytes) and rejection on overflow exist in SDK layer.


950-959: Memo propagation enabled for stealth transfers

output_memo wiring looks correct. Recommend tests covering both Message and Bytes variants and dry-run behavior.


542-547: Ensure encrypted_data consistency in claim flow
ClaimBurnOutputData is built from the original claimed_encrypted_data, yet generate_transfer_statement uses a freshly re-encrypted value in output_statement; unify to a single source—either pass output_statement.statement.encrypted_data into ClaimBurnOutputData or confirm both encryptions are identical.

Comment thread applications/tari_walletd/src/handlers/accounts.rs
Comment thread crates/wallet/crypto/src/memo.rs
@sdbondi
sdbondi merged commit 2d97905 into tari-project:development Oct 10, 2025
11 of 12 checks passed

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 0

🧹 Nitpick comments (5)
crates/wallet/crypto/src/memo.rs (1)

136-158: Consider clarifying the helper function's purpose.

The read_until_len_or_eof function is used only for the unknown variant fallback case. Consider adding a doc comment explaining this specialized use case to aid future maintainers.

+/// Reads up to `max_len` bytes from reader, stopping at EOF if reached earlier.
+/// Used for unknown memo variant fallback to preserve forward compatibility.
 fn read_until_len_or_eof<R: io::Read>(mut buf: &mut [u8], reader: &mut R, max_len: usize) -> io::Result<usize> {
crates/wallet/crypto/src/encrypted_data.rs (3)

126-130: Consider the privacy implications of variable-size encrypted data.

The current implementation allocates max_size for any memo (regardless of actual size) and min_size for no memo. This leaks information about memo presence but not its actual size.

Consider whether constant-size encrypted data (always max_size) would better protect privacy by preventing memo presence detection.


157-157: Clarify the invariant in the expect message.

The expect message could be more specific about what invariant is being checked.

Apply this diff:

-            Ok(EncryptedData::try_from(bytes).expect("bytes length <= EncryptedData::max_size()"))
+            Ok(EncryptedData::try_from(bytes).expect("invariant violation: bytes length exceeds EncryptedData::max_size()"))

173-195: Consider simplifying error handling.

The repeated ok_or_else calls with closure-constructed error messages could be simplified using a helper or by chaining with ? and mapping errors at a higher level. However, the current approach is clear and functional.

crates/wallet/sdk/src/apis/stealth_transfer.rs (1)

883-884: Consider adding a documentation comment for the output_memo field.

While the field is correctly added, a doc comment would help clarify its purpose and constraints (e.g., max size, encryption, recipient-only readability).

Example:

+    /// Optional memo to include in the output. This memo is encrypted and can only be read by the recipient.
+    /// Maximum size is determined by the Memo type (253 bytes).
     pub output_memo: Option<Memo>,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b85c7a2 and 25879dd.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (28)
  • applications/tari_wallet_cli/src/command/transaction.rs (4 hunks)
  • applications/tari_walletd/src/handlers/accounts.rs (6 hunks)
  • applications/tari_walletd/src/handlers/confidential.rs (4 hunks)
  • bindings/src/types/wallet-daemon-client/UtxoInfo.ts (1 hunks)
  • clients/wallet_daemon_client/src/types.rs (6 hunks)
  • crates/template_abi/src/rust.rs (0 hunks)
  • crates/template_lib/src/prelude.rs (1 hunks)
  • crates/template_lib_types/src/lib.rs (1 hunks)
  • crates/template_test_tooling/src/support/confidential.rs (1 hunks)
  • crates/template_test_tooling/src/support/stealth.rs (1 hunks)
  • crates/wallet/crypto/Cargo.toml (1 hunks)
  • crates/wallet/crypto/src/confidential.rs (1 hunks)
  • crates/wallet/crypto/src/encrypted_data.rs (6 hunks)
  • crates/wallet/crypto/src/lib.rs (1 hunks)
  • crates/wallet/crypto/src/memo.rs (1 hunks)
  • crates/wallet/crypto/src/stealth.rs (1 hunks)
  • crates/wallet/crypto/src/unblinded_statement.rs (2 hunks)
  • crates/wallet/crypto/tests/output_statement.rs (3 hunks)
  • crates/wallet/crypto/tests/viewable_balance_proof.rs (1 hunks)
  • crates/wallet/sdk/Cargo.toml (1 hunks)
  • crates/wallet/sdk/src/apis/confidential_crypto.rs (3 hunks)
  • crates/wallet/sdk/src/apis/confidential_transfer.rs (7 hunks)
  • crates/wallet/sdk/src/apis/stealth_crypto.rs (3 hunks)
  • crates/wallet/sdk/src/apis/stealth_transfer.rs (10 hunks)
  • crates/wallet/sdk/src/models/confidential_output.rs (2 hunks)
  • crates/wallet/sdk/src/models/stealth_output.rs (2 hunks)
  • crates/wallet/sdk/tests/base_layer_compat.rs (1 hunks)
  • crates/wallet/sdk/tests/crypto_api.rs (2 hunks)
💤 Files with no reviewable changes (1)
  • crates/template_abi/src/rust.rs
🚧 Files skipped from review as they are similar to previous changes (15)
  • crates/wallet/crypto/src/confidential.rs
  • crates/wallet/crypto/src/lib.rs
  • crates/template_lib/src/prelude.rs
  • crates/wallet/sdk/Cargo.toml
  • crates/wallet/sdk/tests/crypto_api.rs
  • crates/wallet/crypto/src/stealth.rs
  • crates/wallet/crypto/src/unblinded_statement.rs
  • crates/template_test_tooling/src/support/stealth.rs
  • applications/tari_wallet_cli/src/command/transaction.rs
  • crates/wallet/crypto/tests/output_statement.rs
  • crates/template_lib_types/src/lib.rs
  • crates/wallet/sdk/tests/base_layer_compat.rs
  • applications/tari_walletd/src/handlers/confidential.rs
  • applications/tari_walletd/src/handlers/accounts.rs
  • crates/template_test_tooling/src/support/confidential.rs
🧰 Additional context used
🧬 Code graph analysis (11)
bindings/src/types/wallet-daemon-client/UtxoInfo.ts (1)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/crypto/tests/viewable_balance_proof.rs (2)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
crates/wallet/crypto/src/encrypted_data.rs (5)
crates/wallet/crypto/src/unblinded_statement.rs (4)
  • memo (68-70)
  • value (60-62)
  • mask (64-66)
  • new (40-42)
crates/wallet/sdk/src/apis/confidential_crypto.rs (2)
  • unblind_output (105-121)
  • new (26-28)
crates/wallet/crypto/src/kdfs.rs (1)
  • encrypted_data_dh_kdf_aead (32-44)
crates/template_lib_types/src/encrypted_data.rs (4)
  • max_size (30-32)
  • min_size (26-28)
  • len (34-36)
  • as_bytes (42-44)
crates/wallet/crypto/src/memo.rs (5)
  • decode_from (101-133)
  • len (61-66)
  • new_message (42-46)
  • as_bytes (54-59)
  • new_bytes (48-52)
crates/wallet/crypto/src/memo.rs (2)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (68-70)
crates/template_lib_types/src/encrypted_data.rs (2)
  • max_size (30-32)
  • min_size (26-28)
crates/wallet/sdk/src/models/confidential_output.rs (3)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (68-70)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/sdk/src/apis/stealth_transfer.rs (2)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (68-70)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
crates/wallet/sdk/src/apis/confidential_crypto.rs (2)
crates/wallet/crypto/src/encrypted_data.rs (3)
  • decrypt_data (65-79)
  • encrypt_data (54-63)
  • unblind_output (30-52)
crates/wallet/crypto/src/kdfs.rs (1)
  • encrypted_data_dh_kdf_aead (32-44)
crates/wallet/sdk/src/apis/confidential_transfer.rs (1)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (68-70)
crates/wallet/sdk/src/apis/stealth_crypto.rs (2)
crates/wallet/crypto/src/encrypted_data.rs (2)
  • encrypt_data (54-63)
  • unblind_output (30-52)
crates/wallet/sdk/src/apis/confidential_crypto.rs (1)
  • unblind_output (105-121)
crates/wallet/sdk/src/models/stealth_output.rs (3)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (68-70)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
clients/wallet_daemon_client/src/types.rs (4)
crates/wallet/crypto/src/unblinded_statement.rs (1)
  • memo (68-70)
applications/tari_walletd/web_ui/src/components/Memo.tsx (1)
  • Memo (9-22)
bindings/src/types/Memo.ts (1)
  • Memo (3-3)
bindings/src/types/EncryptedData.ts (1)
  • EncryptedData (7-7)
⏰ 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). (5)
  • GitHub Check: check nightly
  • GitHub Check: machete
  • GitHub Check: clippy
  • GitHub Check: test
  • GitHub Check: check stable
🔇 Additional comments (21)
crates/wallet/crypto/Cargo.toml (1)

17-35: Dependencies and feature gating look solid.

argon2 addition and the new serde/ts optional deps with matching feature flags are consistent with the memo serialization work. Nice tidy integration.

crates/wallet/crypto/tests/viewable_balance_proof.rs (1)

15-15: LGTM! Import path refactored correctly.

The import of EncryptedData and Amount from tari_template_lib::types aligns with the reorganization described in the PR. Usage at line 26 remains unchanged and appropriate for these balance proof tests.

bindings/src/types/wallet-daemon-client/UtxoInfo.ts (2)

3-3: LGTM!

The type-only import for Memo is correctly structured and follows TypeScript best practices for importing types in a bindings file.


11-11: Approve memo field addition; 253-byte limit validated in Rust
The Rust code defines MAX_BYTES_LENGTH = 253 in crates/wallet/crypto/src/memo.rs and existing tests cover this logic.

clients/wallet_daemon_client/src/types.rs (1)

43-43: LGTM! Consistent memo field additions across public request/response types.

The optional memo fields have been added consistently to ProofsGenerateRequest, ConfidentialTransferRequest, StealthTransferRequest, and UtxoInfo. The serde attributes (default, skip_serializing_if) ensure backward compatibility and clean JSON serialization.

Also applies to: 495-496, 550-551, 1068-1069, 1110-1110

crates/wallet/sdk/src/models/confidential_output.rs (1)

6-10: LGTM! Model updated with memo field and aligned imports.

The addition of the memo field to ConfidentialOutputModel is consistent with the PR's objectives. The import reorganization (moving EncryptedData from models to types) correctly reflects the refactoring mentioned in the AI summary.

Also applies to: 26-26

crates/wallet/sdk/src/models/stealth_output.rs (1)

4-8: LGTM! Stealth output model updated consistently.

The changes mirror those in confidential_output.rs, maintaining consistency across output models. The memo field addition and import path updates are appropriate.

Also applies to: 26-26

crates/wallet/sdk/src/apis/stealth_crypto.rs (1)

13-18: LGTM! Crypto API extended with memo support.

The API changes correctly thread memo through encryption (encrypt_value_and_mask) and add skip_memo control to decryption (decrypt_value_and_mask). The return type change from MaskAndValue to DecryptedData appropriately encapsulates the additional memo information.

Also applies to: 103-114, 130-146

crates/wallet/sdk/src/apis/confidential_transfer.rs (1)

13-13: LGTM! Memo properly threaded through confidential transfer flow.

The memo parameter is correctly propagated from ConfidentialTransferParams through create_confidential_proof_statement to the encryption layer. The intentional use of None for change outputs (Line 341, 360) is appropriate since change is internal and doesn't need a recipient-visible memo.

Also applies to: 317-322, 337-342, 356-360, 428-458, 494-496

crates/wallet/crypto/src/memo.rs (1)

1-239: LGTM! Well-designed memo implementation with appropriate constraints.

The implementation correctly enforces the 253-byte limit and provides a compact encoding format to maximize payload space within the 255-byte EncryptedData constraint. Key strengths:

  • Custom encoding (tag + 1-byte length + data) saves 3 bytes vs borsh's u32 length prefix
  • UTF-8 validation for Message variant ensures text safety
  • Forward compatibility via unknown variant fallback to Bytes
  • Comprehensive test coverage including edge cases (max size, empty, invalid length, unknown tags)
  • Doc comment (Lines 38-40) correctly states the calculation
crates/wallet/sdk/src/apis/confidential_crypto.rs (1)

8-20: LGTM! Confidential crypto API updated consistently with memo support.

The API changes mirror those in stealth_crypto.rs, maintaining consistency across the crypto layer. Key updates:

  • encrypt_value_and_mask now accepts memo: Option<&Memo> (Line 73)
  • extract_value_and_mask renamed to decrypt_output_data with skip_memo parameter and DecryptedData return type (Lines 80-89)
  • unblind_output extended with skip_memo parameter and DecryptedData return type (Lines 111-121)

The symmetry between confidential and stealth crypto APIs is well-maintained.

Also applies to: 67-78, 80-89, 105-121

crates/wallet/crypto/src/encrypted_data.rs (5)

30-52: LGTM!

The function correctly threads through the skip_memo parameter and properly validates the decrypted commitment against the output commitment. The error handling for overflow and commitment mismatch is appropriate.


54-63: LGTM!

Clean delegation to encrypt_data_inner with proper memo parameter threading.


65-79: LGTM!

Proper conversion of the decryption result into the DecryptedData structure.


216-224: LGTM!

The memo decoding logic correctly handles both the skip_memo flag and empty memo bytes. The helpful comment at line 219 clarifies that remaining bytes are discarded.


236-304: LGTM!

Excellent test coverage including:

  • Encryption/decryption without memo
  • Various memo types (message, bytes, empty, max-size)
  • skip_memo flag behavior
  • Verification that plaintext is not present in ciphertext
crates/wallet/sdk/src/apis/stealth_transfer.rs (5)

451-463: LGTM!

Correctly passes params.output_memo.as_ref() to the primary output statement, enabling recipients to read the sender's memo.


477-494: LGTM!

Fee change outputs correctly use None for memo, as fee-related outputs don't require recipient messages.

Also applies to: 504-510


756-767: LGTM!

Change outputs correctly use None for memo since they're returned to the sender, not a recipient.


769-804: LGTM!

The function signature correctly accepts the memo parameter and properly stores it in the StealthOutputModel at line 796.


806-867: LGTM!

The memo parameter is correctly threaded through to encrypt_value_and_mask at line 836, enabling encryption of the memo alongside the value and mask.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants