fix(lite): address adversarial audit findings in session's lite work
Re-audited this session's lite-wallet changes (originally written at medium
effort) and fixed the genuine issues found:
- walletReady (open path): litelib_initialize_existing returns the bare string
"OK", which is NOT valid JSON, so the previous `json::accept(value)` check
marked a *successful* open as not-ready. Key off a non-empty success response
instead (the bridge already maps "Error:"/null to failure). Drops the now
unused nlohmann include.
- sync progress: while the detached sync thread is still running, syncDone_ is
authoritative — don't surface the backend's transient idle syncstatus
({"syncing":"false"} -> parser progress=1.0/complete=true) as a misleading
100%/done. Force complete=false and zero the bogus 1.0 in the progress model.
- per-address balance: also exclude `pending` outputs (notes/utxos from an
unconfirmed received tx) so per-address figures match confirmed/available.
- secret wiping: the settings page left the page-local request copies
(input.request.*Request.{passphrase,seedPhrase}) unwiped, and the
validation-only fallback path wiped nothing. Replace the single-path memzero
with an RAII scrubber that wipes both the UI char buffers and the request
string copies on every return path.
- concurrency: document that concurrent bridge->execute() is intentionally
unguarded — litelib serializes wallet access internally via
Arc<RwLock<LightWallet>>, so a C++ mutex is unnecessary and would defeat the
sync/syncstatus concurrency the design relies on. syncLaunched_ -> atomic.
Tests: fake backend now returns the real init shapes (seed object for
create/restore, bare "OK" for open) and a new open-path case guards the
walletReady regression. Removed an unreliable alloc==freed leak assert from the
thread-bearing controller test (kept in the thread-free bridge test). Also fixed
a stray CMake indent and removed ~220MB of untracked build/debug scratch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -125,6 +125,14 @@ private:
|
||||
// The bridge is shared (not just owned) so the detached, uninterruptible sync thread can
|
||||
// safely outlive the controller: it holds a ref, so the underlying bridge is destroyed
|
||||
// (and litelib_shutdown called) only once BOTH the controller and a running sync release it.
|
||||
//
|
||||
// Concurrent execute() is intentionally NOT guarded by a C++ mutex here. The design relies
|
||||
// on overlapping calls (the detached sync thread runs the long `sync` while the refresh
|
||||
// worker polls `syncstatus`, and the UI thread may call `new`). litelib serializes wallet
|
||||
// access internally: LightClient.wallet is an Arc<RwLock<LightWallet>> (do_new_address takes
|
||||
// write(), do_balance takes read(), do_sync_internal takes read()/write() and writes
|
||||
// sync_status separately), so concurrent execute() calls cannot corrupt state. A coarse
|
||||
// mutex would instead serialize sync against syncstatus polling and defeat the design.
|
||||
std::shared_ptr<LiteClientBridge> bridge_;
|
||||
LiteWalletLifecycleService lifecycle_;
|
||||
LiteWalletGateway gateway_;
|
||||
@@ -136,7 +144,7 @@ private:
|
||||
|
||||
// Detached background sync (backend `sync` is a blocking, uninterruptible full scan).
|
||||
std::thread syncThread_;
|
||||
bool syncLaunched_ = false;
|
||||
std::atomic<bool> syncLaunched_{false}; // startSync() guard (set on the main thread)
|
||||
std::shared_ptr<std::atomic<bool>> syncDone_ = std::make_shared<std::atomic<bool>>(false);
|
||||
|
||||
// Joinable background refresh worker (fast iterations: syncstatus, plus data once synced).
|
||||
|
||||
Reference in New Issue
Block a user