feat(wallets): recursive subdir scan + wallet.dat encryption/seed badges

Search user-added wallet folders RECURSIVELY (bounded: depth/hit/visit caps,
skip_permission_denied, no symlink-follow, node-junk + node-subtree pruning);
the datadir stays top-level. r.dir is each file's real parent so Open/import
resolve subdir wallets. Auto-suffix the import destination on a name collision.

Add util/wallet_file_probe.h: read encryption/seed/shielded flags straight off
a wallet.dat WITHOUT loading it — validate the Berkeley DB btree magic, then a
bounded streaming byte-scan for the length-prefixed record markers the daemon
writes (mkey -> encrypted; hdseed/hdchain -> seed/HD; zkey/sapzkey -> shielded).
Reads no key material. The Wallets dialog shows Encrypted / Seed / Legacy badges,
plus an Unknown badge when a large file couldn't be fully scanned (so absence of
a lock never falsely reads as unencrypted). Unit-tested + validated read-only
against real wallets.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-11 12:32:59 -05:00
parent 57147fd06b
commit 112c581247
4 changed files with 319 additions and 22 deletions

View File

@@ -262,6 +262,10 @@ void I18n::loadBuiltinEnglish()
strings_["wallets_col_opened"] = "Last opened";
strings_["wallets_current"] = "current";
strings_["wallets_active"] = "Active";
strings_["wallets_badge_encrypted"] = "Encrypted (passphrase-protected)";
strings_["wallets_badge_seed"] = "Seed phrase wallet (HD)";
strings_["wallets_badge_legacy"] = "Legacy wallet (no seed phrase)";
strings_["wallets_badge_unknown"] = "Wallet type not fully determined (large file — open to confirm)";
strings_["wallets_open"] = "Open";
strings_["wallets_never"] = "Never";
strings_["wallets_import_hint"] = "Import to open";