Skip to content

fix(database): resolve remaining error-handling inconsistencies - #614

Merged
PastaPastaPasta merged 2 commits into
dashpay:v1.0-devfrom
thepastaclaw:fix/issue-612-error-handling
Feb 23, 2026
Merged

fix(database): resolve remaining error-handling inconsistencies#614
PastaPastaPasta merged 2 commits into
dashpay:v1.0-devfrom
thepastaclaw:fix/issue-612-error-handling

Conversation

@thepastaclaw

@thepastaclaw thepastaclaw commented Feb 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes the remaining error-handling inconsistencies tracked in #612 after #562:

  • Replaces panic-prone expect/unwrap paths in insert_or_update_contenders with typed error propagation (FromSqlConversionFailure + CorruptedBlobError context).
  • Replaces three InvalidParameterName conversions in wallet::get_wallets with FromSqlConversionFailure:
    • ExtendedPubKey decode failure
    • seed_hash wrong length
    • open-wallet seed wrong length
  • Updates withdrawal display text to avoid a misleading "Remaining Today" value when 24h usage is unavailable.

Tests

  • Added three unit tests in src/database/wallet.rs to verify the FromSqlConversionFailure variants for the three wallet conversion paths.
  • Ran:
    • cargo fmt --all
    • scripts/safe-cargo.sh test --all-features --workspace
    • scripts/safe-cargo.sh test --doc --all-features --workspace
    • scripts/safe-cargo.sh fmt --all -- --check
    • scripts/safe-cargo.sh clippy --all-features --all-targets -- -D warnings

Closes #612

Summary by CodeRabbit

  • Bug Fixes

    • Improved database error handling to prevent crashes and handle corrupted or missing records more gracefully.
    • Withdrawal display now shows "N/A (24h usage data unavailable)" for remaining daily usage when 24-hour data is missing.
  • Tests

    • Added tests validating handling of malformed wallet and contender data to ensure stability.

Validation

What was tested:

  • cargo fmt --all — formatting check
  • scripts/safe-cargo.sh clippy --all-features --all-targets -- -D warnings — lint with zero warnings
  • scripts/safe-cargo.sh test --all-features --workspace — full workspace test suite including 3 new unit tests for FromSqlConversionFailure variants
  • scripts/safe-cargo.sh test --doc --all-features --workspace — doc tests

Results:

  • All local commands passed with zero warnings/errors
  • Clippy CI check — pass (4m20s)
  • Test Suite CI check — pass (7m42s)

Environment: Local macOS arm64; GitHub Actions CI (ubuntu-latest)

@coderabbitai

coderabbitai Bot commented Feb 21, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR replaces panic-prone unwrap/expect calls with explicit error propagation in database code, standardizes rusqlite error variants to use FromSqlConversionFailure with CorruptedBlobError, adds tests for malformed wallet rows, and changes the "Remaining Today" withdrawal display to indicate 24h usage data is unavailable.

Changes

Cohort / File(s) Summary
Database: contested names
src/database/contested_names.rs
Replaces unwrap/expect deserialization and field accesses with explicit error mapping to rusqlite::Error::FromSqlConversionFailure carrying CorruptedBlobError; propagates errors instead of panicking.
Database: wallets & tests
src/database/wallet.rs
Replaces InvalidParameterName error uses with FromSqlConversionFailure + CorruptedBlobError for epk/seed conversions; replaces expects with map_err paths; adds test helpers and tests inserting malformed wallet rows to assert the new error behavior.
Backend display
src/backend_task/platform_info.rs
Removes misleading computed "Remaining Today" value and replaces it with a static message "N/A (24h usage data unavailable)".

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through code with careful cheer,
Replaced the panics that once were near,
Now blobs are checked and wallet faults named,
The withdraw line honestly proclaimed,
A tidy burrow, no surprises here.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(database): resolve remaining error-handling inconsistencies' accurately summarizes the main changes across database files and backend platform info.
Linked Issues check ✅ Passed The PR implements all coding requirements from issue #612: converts panic-prone calls in contested_names.rs to error propagation, replaces InvalidParameterName with FromSqlConversionFailure in wallet.rs, and fixes misleading UI text in platform_info.rs.
Out of Scope Changes check ✅ Passed All changes directly address the three requirements specified in issue #612; no out-of-scope modifications detected in the provided summaries.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 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.

🧹 Nitpick comments (1)
src/database/wallet.rs (1)

881-883: Remaining .expect() on wallet seed hash in identity loading path.

This try_into().expect(...) performs the same [u8; 32] conversion that was just fixed with FromSqlConversionFailure on line 454. A corrupted wallet column in the identity table would panic here.

Suggested fix for consistency
             let wallet_seed_hash_array: [u8; 32] = wallet_seed_hash
                 .try_into()
-                .expect("Seed hash should be 32 bytes");
+                .map_err(|_| {
+                    rusqlite::Error::FromSqlConversionFailure(
+                        1,
+                        rusqlite::types::Type::Blob,
+                        Box::new(CorruptedBlobError(
+                            "Identity wallet seed hash should be 32 bytes".to_string(),
+                        )),
+                    )
+                })?;

Based on learnings, error handling refactoring is needed across the codebase to avoid panics with .expect() and instead propagate errors properly using the ? operator.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/database/wallet.rs` around lines 881 - 883, Replace the panicking
conversion of wallet_seed_hash into [u8; 32] in the identity loading path by
returning an error instead of calling .expect; change the code that sets
wallet_seed_hash_array (currently using wallet_seed_hash.try_into().expect("Seed
hash should be 32 bytes")) to use try_into().map_err(|e|
FromSqlConversionFailure::new("wallet seed hash", Box::new(e)))? (or the
crate-specific FromSqlConversionFailure helper used earlier) and propagate the
error with ? so the function (e.g., the identity loading function) returns
Result instead of panicking; ensure you reference the same error
type/constructor used at the earlier conversion fix around
FromSqlConversionFailure to keep behavior consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/database/wallet.rs`:
- Around line 881-883: Replace the panicking conversion of wallet_seed_hash into
[u8; 32] in the identity loading path by returning an error instead of calling
.expect; change the code that sets wallet_seed_hash_array (currently using
wallet_seed_hash.try_into().expect("Seed hash should be 32 bytes")) to use
try_into().map_err(|e| FromSqlConversionFailure::new("wallet seed hash",
Box::new(e)))? (or the crate-specific FromSqlConversionFailure helper used
earlier) and propagate the error with ? so the function (e.g., the identity
loading function) returns Result instead of panicking; ensure you reference the
same error type/constructor used at the earlier conversion fix around
FromSqlConversionFailure to keep behavior consistent.

Copilot AI 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.

Pull request overview

This PR finishes addressing the remaining error-handling inconsistencies tracked in #612 by replacing panic-prone paths and aligning DB conversion errors on rusqlite::Error::FromSqlConversionFailure, plus clarifying a misleading withdrawal display line when 24h usage data isn’t available.

Changes:

  • Replaced InvalidParameterName-based conversion errors in Database::get_wallets with FromSqlConversionFailure carrying CorruptedBlobError context.
  • Removed remaining expect/unwrap panics in insert_or_update_contenders by propagating typed conversion errors instead.
  • Updated withdrawal info formatting to display “Remaining Today” as N/A when 24h usage data is unavailable, and added unit tests covering the wallet conversion error variants.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/database/wallet.rs Standardizes wallet row conversion failures to FromSqlConversionFailure and adds unit tests validating those variants.
src/database/contested_names.rs Replaces panic paths in contender/document extraction with error propagation using CorruptedBlobError context.
src/backend_task/platform_info.rs Avoids misleading “Remaining Today” output when actual 24h usage can’t be computed.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…tity loading

Address CodeRabbit nitpick: the wallet seed hash conversion in the
identity loading path (get_wallets) still used .expect() which would
panic on corrupted data. Replace with the same FromSqlConversionFailure
+ CorruptedBlobError pattern used for the other three conversions.
@thepastaclaw

Copy link
Copy Markdown
Collaborator Author

Good catch @coderabbitai — addressed the remaining .expect() in the identity loading path in 3aedd88. Now all four seed hash conversions use the same FromSqlConversionFailure + CorruptedBlobError pattern consistently.

@coderabbitai

coderabbitai Bot commented Feb 22, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@thepastaclaw Thank you for addressing this! The consistent use of FromSqlConversionFailure + CorruptedBlobError across all four seed hash conversions is exactly the right approach. This standardization improves error handling clarity and aligns with the overall error propagation strategy throughout the database module. Great work! 🎉


🧠 Learnings used
Learnt from: lklimek
Repo: dashpay/dash-evo-tool PR: 561
File: src/database/identities.rs:151-152
Timestamp: 2026-02-17T08:40:05.750Z
Learning: In `src/database/identities.rs`, the `get_local_qualified_identities` and `get_local_qualified_identities_in_wallets` functions intentionally abort (via `map_err(...)?`) on the first corrupted identity blob rather than skipping it, because corrupted identity data could lead to fund loss. A separate repair feature would be needed to handle such corruption.

Learnt from: lklimek
Repo: dashpay/dash-evo-tool PR: 561
File: src/database/identities.rs:303-304
Timestamp: 2026-02-17T08:39:24.734Z
Learning: In `src/database/identities.rs`, the `get_local_voting_identities` and `get_local_user_identities` functions intentionally abort (via `.collect()`) on the first corrupted identity blob rather than skipping it, because corrupted voting or user identity data could lead to fund loss. A separate repair feature would be needed to handle such corruption.

Learnt from: lklimek
Repo: dashpay/dash-evo-tool PR: 264
File: src/database/wallet.rs:95-99
Timestamp: 2025-05-13T06:55:34.019Z
Learning: Error handling refactoring is needed across the Dash-EVO-Tool (DET) codebase, particularly to avoid panics with `.expect()` and instead propagate errors properly using the `?` operator.

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@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.

🧹 Nitpick comments (1)
src/database/wallet.rs (1)

1313-1411: Three tests cover the three targeted paths correctly; consider adding coverage for the identity seed-hash path.

The three new tests exercise each FromSqlConversionFailure variant with the right preconditions. One small gap: lines 881–889 (identity wallet seed hash) are also changed in this PR but have no corresponding test.

🧪 Suggested test skeleton for the identity seed-hash path
+ #[test]
+ fn test_get_wallets_invalid_identity_seed_hash_uses_from_sql_conversion_failure() {
+     let db = create_test_database().expect("Failed to create test database");
+     let seed_hash = create_test_seed_hash();
+     let valid_epk = create_test_master_epk_bytes(Network::Testnet);
+
+     // Insert a valid wallet row
+     {
+         let conn = db.conn.lock().unwrap();
+         conn.execute(
+             "INSERT INTO wallet (seed_hash, encrypted_seed, salt, nonce,
+              master_ecdsa_bip44_account_0_epk, alias, is_main, uses_password,
+              password_hint, network, confirmed_balance, unconfirmed_balance, total_balance)
+              VALUES (?, ?, ?, ?, ?, NULL, 0, 0, NULL, 'testnet', 0, 0, 0)",
+             rusqlite::params![
+                 seed_hash.as_slice(),
+                 vec![0u8; 64],
+                 vec![0u8; 16],
+                 vec![0u8; 12],
+                 valid_epk,
+             ],
+         ).unwrap();
+         // Insert an identity row with a wallet seed_hash blob that is only 31 bytes
+         conn.execute(
+             "INSERT INTO identity (data, wallet, wallet_index, network)
+              VALUES (?, ?, ?, 'testnet')",
+             rusqlite::params![vec![0u8; 4], vec![0u8; 31], 0u32],
+         ).unwrap();
+     }
+
+     let err = db.get_wallets(&Network::Testnet).expect_err("expected failure");
+     match err {
+         rusqlite::Error::FromSqlConversionFailure(1, rusqlite::types::Type::Blob, _) => {}
+         _ => panic!("unexpected error variant: {}", err),
+     }
+ }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/database/wallet.rs` around lines 1313 - 1411, Add a test that exercises
the identity wallet seed-hash failure path by creating a test DB, inserting one
valid wallet row (as in existing tests) and then inserting an identity row whose
wallet blob is an invalid length (e.g., 31 bytes) so that
db.get_wallets(&Network::Testnet) returns
rusqlite::Error::FromSqlConversionFailure for the identity seed-hash conversion;
mirror naming and assertions from other tests (e.g., call it
test_get_wallets_invalid_identity_seed_hash_uses_from_sql_conversion_failure,
use create_test_database(), create_test_seed_hash(),
create_test_master_epk_bytes(), insert into identity with params![vec![0u8;4],
vec![0u8;31], 0u32], then match the error for FromSqlConversionFailure with the
correct column index/type).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/database/wallet.rs`:
- Around line 1313-1411: Add a test that exercises the identity wallet seed-hash
failure path by creating a test DB, inserting one valid wallet row (as in
existing tests) and then inserting an identity row whose wallet blob is an
invalid length (e.g., 31 bytes) so that db.get_wallets(&Network::Testnet)
returns rusqlite::Error::FromSqlConversionFailure for the identity seed-hash
conversion; mirror naming and assertions from other tests (e.g., call it
test_get_wallets_invalid_identity_seed_hash_uses_from_sql_conversion_failure,
use create_test_database(), create_test_seed_hash(),
create_test_master_epk_bytes(), insert into identity with params![vec![0u8;4],
vec![0u8;31], 0u32], then match the error for FromSqlConversionFailure with the
correct column index/type).

@lklimek lklimek 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.

Clean, focused PR. All three changes are correct:

  • contested_names.rs: expect()/unwrap() panics replaced with proper FromSqlConversionFailure error propagation using CorruptedBlobError context
  • wallet.rs: InvalidParameterName misuse replaced with FromSqlConversionFailure at correct column indices; good test coverage for all three paths
  • platform_info.rs: Honest "N/A" instead of misleading daily-limit-as-remaining value

Follow-up commit properly addressed CodeRabbit's identity seed-hash nitpick. No security concerns.

@PastaPastaPasta
PastaPastaPasta merged commit 7c49c9e into dashpay:v1.0-dev Feb 23, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: remaining error handling inconsistencies from PR #562

4 participants