fix(audit): batch 1 — latent unlock/path/rescan correctness bugs
Four correctness fixes surfaced by the codebase audit: 1. Lite address-book path bug: AddressBook::getDefaultPath() hardcoded "ObsidianDragon" while Settings::getDefaultPath() uses DRAGONX_APP_NAME, so the Lite build wrote addressbook.json into the full-node app's config dir instead of ObsidianDragonLite/. Now uses DRAGONX_APP_NAME on all platforms (no macOS path change — the getConfigDir consolidation, which would move the macOS dir, is deferred). 2. PIN-unlock lockout bypass: the PIN path duplicated unlockWallet's success/lockout logic and its RPC-error branch bumped the attempt counter but skipped the escalating-lockout math entirely — a PIN user hitting an RPC error escaped the lockout curve. Extracted App::applyUnlockSuccess() and App::applyUnlockFailure() and routed all unlock paths (passphrase, PIN vault-fail, PIN RPC-fail) through them, so the lockout curve now applies uniformly. (noVault stays a mode-switch, not a failed attempt.) 3. Witness/rescan reset drift: the ~11-line rescan+witness completion reset was copy-pasted at four sites (app.cpp x2, app_network.cpp x2); adding a witness field and missing a copy would leave stale progress. Folded into App::resetWitnessRescanProgress(). Left the distinct phase-transition reset in App::update() untouched (it sets witness_phase, not 0). 4. Truncation underflow: send_tab/receive_tab's size_t TruncateAddress copies computed (maxLen - 3) without guarding maxLen <= 3, which wraps and throws std::out_of_range on short inputs. Added the guard. Full-node + Lite build clean; ctest 1/1; source-hygiene clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
24
src/app.cpp
24
src/app.cpp
@@ -777,17 +777,8 @@ void App::update()
|
||||
// poll (which hits the still-running pre-restart daemon, rescanning=false)
|
||||
// would fire a false "complete" the instant rescan was clicked.
|
||||
ui::Notifications::instance().success("Blockchain rescan complete");
|
||||
state_.sync.rescanning = false;
|
||||
rescan_confirmed_active_ = false;
|
||||
resetWitnessRescanProgress();
|
||||
state_.sync.rescan_progress = 1.0f;
|
||||
state_.sync.rescan_status.clear();
|
||||
state_.sync.building_witnesses = false;
|
||||
state_.sync.witness_phase = 0;
|
||||
state_.sync.witness_progress = 0.0f;
|
||||
state_.sync.witness_remaining = 0;
|
||||
witness_rebuild_total_blocks_ = 0;
|
||||
witness_seen_txids_.clear();
|
||||
witness_total_txs_ = 0;
|
||||
}
|
||||
// else: rescanning=false but not yet confirmed → pre-restart daemon; keep waiting.
|
||||
};
|
||||
@@ -1048,18 +1039,9 @@ void App::update()
|
||||
if (state_.sync.rescanning) {
|
||||
ui::Notifications::instance().success("Blockchain rescan complete");
|
||||
}
|
||||
state_.sync.rescanning = false;
|
||||
rescan_confirmed_active_ = false;
|
||||
state_.sync.rescan_progress = 1.0f;
|
||||
state_.sync.rescan_status.clear();
|
||||
// Witness rebuild finishes with the rescan it's part of.
|
||||
state_.sync.building_witnesses = false;
|
||||
state_.sync.witness_phase = 0;
|
||||
state_.sync.witness_progress = 0.0f;
|
||||
state_.sync.witness_remaining = 0;
|
||||
witness_rebuild_total_blocks_ = 0;
|
||||
witness_seen_txids_.clear();
|
||||
witness_total_txs_ = 0;
|
||||
resetWitnessRescanProgress();
|
||||
state_.sync.rescan_progress = 1.0f;
|
||||
} else if (foundWitness) {
|
||||
// Witness rebuild is the tail phase of a rescan — keep rescanning set so
|
||||
// the broader gating holds, but surface the witness-specific progress.
|
||||
|
||||
Reference in New Issue
Block a user