Skip to content

feat(wallet): add rpc call to allow decrypting utxo values with view keys enabled - #1587

Merged
sdbondi merged 2 commits into
tari-project:developmentfrom
sdbondi:wallet-view-utxo-balance
Sep 25, 2025
Merged

feat(wallet): add rpc call to allow decrypting utxo values with view keys enabled#1587
sdbondi merged 2 commits into
tari-project:developmentfrom
sdbondi:wallet-view-utxo-balance

Conversation

@sdbondi

@sdbondi sdbondi commented Sep 25, 2025

Copy link
Copy Markdown
Member

Description

feat(wallet): add rpc call to allow decrypting utxo values with view keys enabled

Motivation and Context

'stealth_utxos.decrypt_value' method

How Has This Been Tested?

Not tested - code moved from previously working confidential vault decryption

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

    • Added RPC to decrypt and reveal values for selected stealth UTXOs; clients (JS/Rust) support typed request/response.
    • Balance lookups now report only outputs that are viewable with the provided view key.
  • Refactor

    • Simplified stealth UTXO routing and added a dedicated decrypt_value command.
  • Chores

    • Bumped JavaScript wallet daemon client package version to 1.9.2.
  • Security

    • Stealth UTXOs list now requires authentication.

@coderabbitai

coderabbitai Bot commented Sep 25, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Replaces brute-force balance logic with a new ViewableBalanceApi, adds a new stealth_utxos.decrypt_value handler and client bindings, updates handlers to filter viewable balances, exposes UtxoAddress contents, and removes brute-force methods from Confidential/Stealth crypto APIs.

Changes

Cohort / File(s) Summary of changes
Wallet daemon handlers
applications/tari_walletd/src/handlers/confidential.rs, applications/tari_walletd/src/handlers/stealth_utxos.rs
Switches calls from removed brute-force APIs to sdk.viewable_balance_api().try_brute_force_commitment_balances; filters inputs to only include outputs with viewable_balance. Adds handle_decrypt_value (value lookup table loading, range handling, id limits, timed brute-force) and updates handle_list signature/authorization.
JSON-RPC routing
applications/tari_walletd/src/jrpc_server.rs
Adds stealth_utxos.decrypt_value dispatch and flattens stealth_utxos method matching while preserving list.
SDK: new ViewableBalance API
crates/wallet/sdk/src/apis/mod.rs, crates/wallet/sdk/src/apis/viewable_balance.rs, crates/wallet/sdk/src/sdk.rs
Adds viewable_balance module and ViewableBalanceApi with try_brute_force_commitment_balances; exposes accessor WalletSdk::viewable_balance_api(). Introduces ViewableBalanceApiError.
SDK: remove brute-force from other APIs
crates/wallet/sdk/src/apis/confidential_crypto.rs, crates/wallet/sdk/src/apis/stealth_crypto.rs
Removes previous try_brute_force_commitment_balances methods and related imports/error variants (e.g., ValueLookupTableError) from Confidential and Stealth crypto APIs.
Template models
crates/template_lib/src/models/utxo.rs
Adds UtxoAddress::into_contents() and makes UtxoAddressContents.resource_address and .id public.
Rust client API
clients/wallet_daemon_client/src/lib.rs, clients/wallet_daemon_client/src/types.rs
Adds stealth_utxos_decrypt_value(...) RPC method and public types StealthUtxosDecryptValueRequest/Response; expands imported models to include UtxoId.
TypeScript bindings & exports
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueRequest.ts, bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueResponse.ts, bindings/src/wallet-daemon-client.ts
Generates new TS request/response types and re-exports them from wallet-daemon-client bindings.
JavaScript client
clients/javascript/wallet_daemon_client/src/index.ts, clients/javascript/wallet_daemon_client/package.json
Adds stealthUtxosDecryptValue(params) method calling stealth_utxos.decrypt_value and bumps package version from 1.9.1 to 1.9.2.
Other
crates/wallet/sdk/src/apis/viewable_balance.rs (new)
Implements conversion from byte types to ElgamalVerifiableBalance, batched brute-force, and lookup-table error mapping for viewable balances.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client as JS/Rust Client
  participant JRPC as JSON-RPC Server
  participant Handler as stealth_utxos::handlers
  participant SDK as WalletSdk
  participant VB as ViewableBalanceApi
  participant LUT as ValueLookupTable

  Client->>JRPC: stealth_utxos.decrypt_value(request)
  JRPC->>Handler: handle_decrypt_value(token, request)
  Handler->>Handler: validate ids (<=10), build `UtxoAddress` list
  Handler->>Handler: fetch substates for UTXO ids
  Handler->>SDK: derive secret view key
  Handler->>Handler: load LUT (file or AlwaysMiss)
  Handler->>SDK: sdk.viewable_balance_api()
  SDK->>VB: get ViewableBalanceApi
  Handler->>VB: try_brute_force_commitment_balances(secret_view_key, outputs, range, LUT)
  VB->>LUT: batched lookup calls
  LUT-->>VB: lookup results
  VB-->>Handler: Vec<Option<u64>> balances
  Handler-->>JRPC: StealthUtxosDecryptValueResponse { balances }
  JRPC-->>Client: RPC response
  Note over Handler,VB: duration logged around brute-force step
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

I hop through code with eager paws,
I sniff the outputs, count their claws.
Viewable balances peek and play,
A new decrypt route leads the way.
Hooray for hops and clearer days! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 title succinctly describes the primary change—adding an RPC call for decrypting UTXO values using view keys—and aligns directly with the implementation of the stealth_utxos.decrypt_value method and related client and SDK updates. It follows conventional commit style, is clear about the feature scope, and enables team members to understand the pull request’s intent at a glance.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • 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: 5

🧹 Nitpick comments (5)
crates/template_lib/src/models/utxo.rs (1)

127-129: Public fields on UtxoAddressContents

This relaxes encapsulation and matches the TS bindings usage. If future invariants are needed, consider accessor methods or marking the struct non_exhaustive.

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

226-229: New accessor for ViewableBalanceApi — consider a brief doc comment

Add a short doc to surface its purpose in rustdoc.

Apply this diff to document the accessor:

-    pub fn viewable_balance_api(&self) -> ViewableBalanceApi {
+    /// Returns the ViewableBalance API used for decrypting/viewing UTXO balances with view keys.
+    pub fn viewable_balance_api(&self) -> ViewableBalanceApi {
         ViewableBalanceApi
     }
applications/tari_walletd/src/handlers/confidential.rs (1)

288-314: Validate min/max range before brute-force to avoid empty/invalid scans

If minimum_expected_value > maximum_expected_value, the inclusive range becomes empty; better to fail fast with InvalidParams.

You can add a guard before constructing the range (illustrative snippet):

let min = req.minimum_expected_value.unwrap_or(0);
let max = req.maximum_expected_value.unwrap_or(10_000_000_000);
if min > max {
    return Err(invalid_params("minimum_expected_value", Some("must be <= maximum_expected_value")));
}
let value_range = min..=max;
applications/tari_walletd/src/handlers/stealth_utxos.rs (2)

65-71: Limit check is good; consider also validating non-empty.

Optional: reject empty ids early to avoid unnecessary network calls.

-    if req.ids.len() > 10 {
+    if req.ids.is_empty() {
+        return Err(invalid_params("ids", Some("Must request at least one UTXO")));
+    }
+    if req.ids.len() > 10 {
         return Err(invalid_params(
             "ids",
             Some("Cannot request more than 10 UTXOs at a time"),
         ));
     }

103-127: Move blocking file IO and loading into blocking context; prefer spawn_blocking.

File open and lookup loading are blocking; wrap them (and the brute-force) in spawn_blocking to avoid blocking the async scheduler.

-    let balances = match context.config().value_lookup_table_file.as_ref() {
-        Some(file) => {
-            let mut file = fs::File::open(file)
-                .map_err(|e| anyhow!("Unable to load value lookup file '{}': {e}", file.display()))?;
-            let mut lookup = IoReaderValueLookup::load(&mut file)?;
-
-            block_in_place(|| {
-                sdk.viewable_balance_api().try_brute_force_commitment_balances(
-                    &view_key.key,
-                    outputs.values().copied(), // Copying the reference, not the PrivateOutput
-                    value_range,
-                    &mut lookup,
-                )
-            })?
-        },
-        None => block_in_place(|| {
-            sdk.viewable_balance_api().try_brute_force_commitment_balances(
-                &view_key.key,
-                outputs.values().copied(),
-                value_range,
-                &mut AlwaysMissLookupTable,
-            )
-        })?,
-    };
+    let balances = match context.config().value_lookup_table_file.as_ref() {
+        Some(path) => {
+            let view_key = view_key.key.clone();
+            let outputs_iter = outputs.values().copied().collect::<Vec<_>>();
+            let path = path.clone();
+            tokio::task::spawn_blocking(move || {
+                let mut file = fs::File::open(&path)
+                    .map_err(|e| anyhow!("Unable to load value lookup file '{}': {e}", path.display()))?;
+                let mut lookup = IoReaderValueLookup::load(&mut file)?;
+                sdk.viewable_balance_api().try_brute_force_commitment_balances(
+                    &view_key, outputs_iter.iter().copied(), value_range, &mut lookup,
+                )
+            })
+            .await??
+        },
+        None => {
+            let view_key = view_key.key.clone();
+            let outputs_iter = outputs.values().copied().collect::<Vec<_>>();
+            tokio::task::spawn_blocking(move || {
+                sdk.viewable_balance_api().try_brute_force_commitment_balances(
+                    &view_key, outputs_iter.iter().copied(), value_range, &mut AlwaysMissLookupTable,
+                )
+            })
+            .await??
+        },
+    };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e6a2f8a and 112f583.

📒 Files selected for processing (16)
  • applications/tari_walletd/src/handlers/confidential.rs (2 hunks)
  • applications/tari_walletd/src/handlers/stealth_utxos.rs (2 hunks)
  • applications/tari_walletd/src/jrpc_server.rs (1 hunks)
  • bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueRequest.ts (1 hunks)
  • bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueResponse.ts (1 hunks)
  • bindings/src/wallet-daemon-client.ts (2 hunks)
  • clients/javascript/wallet_daemon_client/package.json (1 hunks)
  • clients/javascript/wallet_daemon_client/src/index.ts (4 hunks)
  • clients/wallet_daemon_client/src/lib.rs (2 hunks)
  • clients/wallet_daemon_client/src/types.rs (2 hunks)
  • crates/template_lib/src/models/utxo.rs (2 hunks)
  • crates/wallet/sdk/src/apis/confidential_crypto.rs (0 hunks)
  • crates/wallet/sdk/src/apis/mod.rs (1 hunks)
  • crates/wallet/sdk/src/apis/stealth_crypto.rs (1 hunks)
  • crates/wallet/sdk/src/apis/viewable_balance.rs (1 hunks)
  • crates/wallet/sdk/src/sdk.rs (2 hunks)
💤 Files with no reviewable changes (1)
  • crates/wallet/sdk/src/apis/confidential_crypto.rs
🧰 Additional context used
🧬 Code graph analysis (10)
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueResponse.ts (1)
bindings/src/types/UtxoId.ts (1)
  • UtxoId (3-3)
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueRequest.ts (2)
bindings/src/types/ResourceAddress.ts (1)
  • ResourceAddress (6-6)
bindings/src/types/UtxoId.ts (1)
  • UtxoId (3-3)
crates/wallet/sdk/src/apis/viewable_balance.rs (2)
bindings/src/types/PrivateOutput.ts (1)
  • PrivateOutput (6-11)
crates/engine_types/src/crypto/elgamal.rs (1)
  • batched_brute_force (188-232)
clients/wallet_daemon_client/src/types.rs (4)
bindings/src/types/ResourceAddress.ts (1)
  • ResourceAddress (6-6)
bindings/src/types/UtxoId.ts (1)
  • UtxoId (3-3)
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueRequest.ts (1)
  • StealthUtxosDecryptValueRequest (5-11)
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueResponse.ts (1)
  • StealthUtxosDecryptValueResponse (4-4)
clients/wallet_daemon_client/src/lib.rs (2)
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueRequest.ts (1)
  • StealthUtxosDecryptValueRequest (5-11)
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueResponse.ts (1)
  • StealthUtxosDecryptValueResponse (4-4)
crates/template_lib/src/models/utxo.rs (3)
bindings/src/types/UtxoAddressContents.ts (1)
  • UtxoAddressContents (8-8)
bindings/src/types/ResourceAddress.ts (1)
  • ResourceAddress (6-6)
bindings/src/types/UtxoId.ts (1)
  • UtxoId (3-3)
applications/tari_walletd/src/handlers/stealth_utxos.rs (4)
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueRequest.ts (1)
  • StealthUtxosDecryptValueRequest (5-11)
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueResponse.ts (1)
  • StealthUtxosDecryptValueResponse (4-4)
crates/template_lib/src/models/utxo.rs (2)
  • id (35-37)
  • new (27-29)
applications/tari_walletd/src/handlers/context.rs (1)
  • new (35-53)
applications/tari_walletd/src/jrpc_server.rs (2)
applications/tari_swarm_daemon/src/webserver/server.rs (1)
  • call_handler (154-178)
applications/tari_walletd/src/handlers/stealth_utxos.rs (2)
  • handle_list (29-55)
  • handle_decrypt_value (57-134)
clients/javascript/wallet_daemon_client/src/index.ts (2)
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueRequest.ts (1)
  • StealthUtxosDecryptValueRequest (5-11)
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueResponse.ts (1)
  • StealthUtxosDecryptValueResponse (4-4)
crates/wallet/sdk/src/apis/stealth_crypto.rs (1)
crates/engine_types/src/crypto/helpers.rs (1)
  • get_commitment_factory (50-52)
⏰ 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: clippy
  • GitHub Check: test
  • GitHub Check: check stable
  • GitHub Check: machete
🔇 Additional comments (16)
clients/javascript/wallet_daemon_client/package.json (1)

3-3: Version bump looks good

Incrementing to 1.9.2 matches the new RPC surface you added for stealth UTXO decryption and keeps the published client aligned with the new API.

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

18-18: Expose ViewableBalance API module — LGTM

Module exposure is clear and consistent with SDK accessors added elsewhere.

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

9-9: Confirm ConvertFromByteType import path

Ensure ConvertFromByteType is re-exported from tari_engine_types for the Ristretto types; if not, import it from the crate that actually defines the trait impls to avoid trait not in scope errors.

crates/template_lib/src/models/utxo.rs (1)

39-41: into_contents addition — LGTM

Consuming extractor is useful and avoids clones. Call sites (e.g., walletd handlers) benefit directly.

applications/tari_walletd/src/jrpc_server.rs (1)

185-189: Add stealth_utxos.decrypt_value route — LGTM

Routing cleanly wires to the new handler; default not-found fallback preserved.

Please ensure this method is included in any RPC discovery/metadata exposed to clients so it shows up in feature introspection.

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

39-39: New API import — LGTM

Import aligns with added accessor.

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

30-30: LGTM: new public type exports wired

Re-exports for StealthUtxosDecryptValueRequest/Response are correctly added and consistent with existing patterns.

Also applies to: 112-112

clients/javascript/wallet_daemon_client/src/index.ts (1)

353-355: LGTM: new RPC method added and correctly routed

Method name matches server route ("stealth_utxos.decrypt_value") and follows existing list pattern.

Ensure callers pass BigInt-compatible params and that the transport serializes BigInt safely (see related comments on bindings types).

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

297-305: LGTM: switch to viewable_balance_api for brute-force balance lookup

The API swap is appropriate and keeps the heavy work inside block_in_place. No functional regressions apparent.

Please confirm try_brute_force_commitment_balances from viewable_balance_api preserves previous semantics (output ordering/length matching the commitments iterator) to keep the keys().zip(balances) mapping correct.

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

4-4: Use Partial<Record<UtxoId, bigint | null>> for balances and add BigInt-safe JSON handling

bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueResponse.ts:

-export type StealthUtxosDecryptValueResponse = { balances: { [key in UtxoId]?: bigint | null } };
+export type StealthUtxosDecryptValueResponse = { balances: Partial<Record<UtxoId, bigint | null>> };

No BigInt-safe JSON transport found—verify JSON.stringify/parse uses a replacer/reviver for BigInt values.

clients/wallet_daemon_client/src/lib.rs (2)

117-119: Re-exports look correct.

New request/response types are properly wired into the client’s type surface.


484-489: Confirm RPC route registration and bindings

  • No occurrences of stealth_utxos.decrypt_value found in applications/tari_walletd/src/jrpc_server.rs, handlers/stealth_utxos.rs, or in the TS bindings.
  • Add the JSON-RPC method registration in jrpc_server.rs, implement the handler in handlers/stealth_utxos.rs, and expose it in the bindings.
clients/wallet_daemon_client/src/types.rs (1)

55-63: Import additions are appropriate.

UtxoId and VaultId exposure matches the new request/response structs below.

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

51-57: Error type design LGTM.

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

81-85: Permissions: Admin may be overly broad. Verify intended scope.

If decrypting with a view key is permissible for users with AccountList or a narrower permission, consider reducing to a more specific permission.

What role should be allowed to decrypt with view keys? If Admin is correct, ignore.


131-133: Potential misalignment between keys and balances if any output has no viewable_balance.

Today, API filters those out, shortening results. After applying the ViewableBalanceApi fix (returning a result per input), this zip remains correct. If not fixing API, pre-filter outputs and include explicit None entries for missing balances.

I recommend the API fix in crates/wallet/sdk/src/apis/viewable_balance.rs to guarantee positional alignment.

Comment thread applications/tari_walletd/src/handlers/stealth_utxos.rs
Comment thread clients/wallet_daemon_client/src/types.rs
Comment thread crates/wallet/sdk/src/apis/viewable_balance.rs Outdated
Comment thread crates/wallet/sdk/src/apis/viewable_balance.rs
@sdbondi
sdbondi merged commit 3950673 into tari-project:development Sep 25, 2025
11 of 12 checks passed
@sdbondi
sdbondi deleted the wallet-view-utxo-balance branch September 25, 2025 10:16

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
applications/tari_walletd/src/handlers/confidential.rs (1)

307-313: Guard against pathological brute force without a lookup table.

With AlwaysMissLookupTable and wide ranges (defaults up to 10B), this path can be prohibitively slow. Add a range-size guard.

-        None => block_in_place(|| {
-            sdk.viewable_balance_api().try_brute_force_commitment_balances(
-                &view_key.key,
-                commitments.values().filter_map(|o| o.viewable_balance.as_ref()),
-                value_range,
-                &mut AlwaysMissLookupTable,
-            )
-        })?,
+        None => {
+            // Prevent runaway work without a lookup table.
+            const MAX_RANGE_WITHOUT_LOOKUP: u64 = 1_000_000;
+            let span = (*value_range.end() - *value_range.start()).saturating_add(1);
+            if span > MAX_RANGE_WITHOUT_LOOKUP {
+                return Err(invalid_params(
+                    "maximum_expected_value",
+                    Some("range too large without a value lookup table; provide config.value_lookup_table_file or reduce the range"),
+                ));
+            }
+            block_in_place(|| {
+                sdk.viewable_balance_api().try_brute_force_commitment_balances(
+                    &view_key.key,
+                    commitments.values().filter_map(|o| o.viewable_balance.as_ref()),
+                    value_range,
+                    &mut AlwaysMissLookupTable,
+                )
+            })?
+        },
🧹 Nitpick comments (2)
crates/wallet/sdk/src/apis/viewable_balance.rs (2)

28-36: Add index to error for precise diagnostics.

When a proof fails to decompress, the error lacks location context. Enumerate and include the index in the error details.

-        let outputs_viewable_balance_decompressed = proofs
-            .map(ElgamalVerifiableBalance::convert_from_byte_type)
-            .collect::<Result<Vec<_>, _>>()
-            .map_err(|_| WalletCryptoError::InvalidArgument {
-                name: "proofs",
-                details: "Malformed viewable balance in output when decompressing ElgamalVerifiableBalance for brute \
-                          forcing"
-                    .to_string(),
-            })?;
+        let outputs_viewable_balance_decompressed = proofs
+            .enumerate()
+            .map(|(i, vb)| {
+                ElgamalVerifiableBalance::convert_from_byte_type(vb).map_err(|_| {
+                    WalletCryptoError::InvalidArgument {
+                        name: "proofs",
+                        details: format!(
+                            "Malformed viewable balance at index {} when decompressing ElgamalVerifiableBalance",
+                            i
+                        ),
+                    }
+                })
+            })
+            .collect::<Result<Vec<_>, _>>()?;

16-27: Generalize iterator bounds for better ergonomics.

Accept IntoIterator for proofs and value_range to support slices, Vec, and ranges uniformly.

-    pub fn try_brute_force_commitment_balances<'a, TLookup, TProofsIter>(
+    pub fn try_brute_force_commitment_balances<'a, TLookup, TProofsIter, IRange>(
         &self,
         secret_view_key: &RistrettoSecretKey,
-        proofs: TProofsIter,
-        value_range: RangeInclusive<u64>,
+        proofs: TProofsIter,
+        value_range: IRange,
         lookup: &mut TLookup,
     ) -> Result<Vec<Option<u64>>, ViewableBalanceApiError>
     where
         TLookup: ValueLookupTable,
-        TProofsIter: Iterator<Item = &'a ElgamalVerifiableBalanceBytes>,
+        TProofsIter: IntoIterator<Item = &'a ElgamalVerifiableBalanceBytes>,
+        IRange: IntoIterator<Item = u64>,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 112f583 and 0dfaeaf.

📒 Files selected for processing (3)
  • applications/tari_walletd/src/handlers/confidential.rs (2 hunks)
  • applications/tari_walletd/src/handlers/stealth_utxos.rs (2 hunks)
  • crates/wallet/sdk/src/apis/viewable_balance.rs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
crates/wallet/sdk/src/apis/viewable_balance.rs (2)
bindings/src/types/ElgamalVerifiableBalanceBytes.ts (1)
  • ElgamalVerifiableBalanceBytes (4-7)
crates/engine_types/src/crypto/elgamal.rs (1)
  • batched_brute_force (188-232)
applications/tari_walletd/src/handlers/confidential.rs (2)
crates/engine_types/src/resource.rs (1)
  • view_key (126-128)
crates/engine_types/src/resource_container.rs (1)
  • commitments (520-533)
applications/tari_walletd/src/handlers/stealth_utxos.rs (5)
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueRequest.ts (1)
  • StealthUtxosDecryptValueRequest (5-11)
bindings/src/types/wallet-daemon-client/StealthUtxosDecryptValueResponse.ts (1)
  • StealthUtxosDecryptValueResponse (4-4)
crates/template_lib/src/models/utxo.rs (2)
  • id (35-37)
  • new (27-29)
applications/tari_walletd/src/handlers/context.rs (1)
  • new (35-53)
crates/wallet/sdk/src/apis/viewable_balance.rs (1)
  • proofs (28-30)
⏰ 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: test
  • GitHub Check: check nightly
  • GitHub Check: check stable
  • GitHub Check: machete
  • GitHub Check: clippy
  • GitHub Check: fmt
🔇 Additional comments (3)
applications/tari_walletd/src/handlers/stealth_utxos.rs (1)

86-87: Still missing an upper bound on the brute-force window.

With AlwaysMissLookupTable this will iterate up to 10 000 000 000 commitments per UTXO, so a single request can DoS the daemon again. Please clamp the min/max span and reject ranges that exceed it.

-    let value_range = req.minimum_expected_value.unwrap_or(0)..=req.maximum_expected_value.unwrap_or(10_000_000_000);
+    const MAX_BRUTE_FORCE_SPAN: u64 = 1_000_000;
+    let min = req.minimum_expected_value.unwrap_or(0);
+    let max = req
+        .maximum_expected_value
+        .unwrap_or(min.saturating_add(MAX_BRUTE_FORCE_SPAN.saturating_sub(1)));
+    if max < min {
+        return Err(invalid_params(
+            "maximum_expected_value",
+            Some("maximum_expected_value must be >= minimum_expected_value"),
+        ));
+    }
+    let span = max.saturating_sub(min) + 1;
+    if span > MAX_BRUTE_FORCE_SPAN {
+        return Err(invalid_params(
+            "maximum_expected_value",
+            Some("value range too wide; supply narrower bounds"),
+        ));
+    }
+    let value_range = min..=max;
crates/wallet/sdk/src/apis/viewable_balance.rs (1)

38-46: LGTM: correct use of batched_brute_force and error mapping.

The call preserves input ordering and propagates lookup errors cleanly.

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

298-304: Confirm BTreeMap preserves iteration order: commitments is a std::collections::BTreeMap, so its values() and iter() methods yield entries in the same (sorted‐by‐key) order.

Comment on lines +319 to 324
balances: commitments
.iter()
.filter_map(|(id, o)| o.viewable_balance.as_ref().map(|_| *id))
.zip(balances)
.collect(),
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Confirm API semantics: dropping non-viewable outputs from response.

This returns balances only for outputs that have a viewable_balance. If clients expect a full list with Nones for non-viewable items, this could be a breaking behavioral change.


🏁 Script executed:

#!/bin/bash
# Inspect response type to confirm expected semantics
rg -n -C3 'struct\s+ConfidentialViewVaultBalanceResponse|type\s+ConfidentialViewVaultBalanceResponse' --type=rust

Length of output: 659


🏁 Script executed:

#!/bin/bash
# Locate handler function and context around the balances construction
rg -n 'ConfidentialViewVaultBalanceResponse' -C8 applications/tari_walletd/src/handlers/confidential.rs
rg -n 'view_vault_balance' -C5 applications/tari_walletd/src/handlers/confidential.rs

Length of output: 2132


Include non-viewable outputs as null in response.
Handler currently drops outputs without a viewable_balance, but ConfidentialViewVaultBalanceResponse.balances: HashMap<PedersenCommitmentBytes, Option<u64>> expects keys with None for non-viewable items. In applications/tari_walletd/src/handlers/confidential.rs:319–324, replace the filter_map(...).zip(balances) logic with an iterator over all commitments, mapping each to Some(balance) or None accordingly.

🤖 Prompt for AI Agents
In applications/tari_walletd/src/handlers/confidential.rs around lines 319-324,
the current code filters out non-viewable outputs and zips only viewable
commitments with balances; instead iterate over all commitments and build the
balances map so each commitment key is present with Some(balance) for viewable
outputs and None for non-viewable ones. Implement this by creating a mutable
iterator over the balances vector and then mapping over commitments: for each
(id, output) if output.viewable_balance.is_some() take the next balance from the
balances iterator and assign Some(balance), otherwise assign None; collect that
into the HashMap<PedersenCommitmentBytes, Option<u64>>.

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