fix: Tier-2 UX robustness — action guards, in-progress guards, input trimming
The app-wide "import-key pattern" fixes (missing guards / untrimmed input / no
re-entrancy protection), from the robustness audit:
- Import-key dialog: unify the type indicator and the RPC dispatch on one
classifier (classifyPrivateKey is now prefix-aware — an "SK..." shielded key no
longer misroutes to the transparent RPC), trim manual input (not just Paste),
and gate both the indicator and the Import button on a shared
isRecognizedPrivateKey() so unrecognized/empty input can't be submitted.
- Shield: guard the submit button on connected + !syncing (like Send), with a
disabled tooltip, instead of firing into a raw daemon error.
- Mining: disable the pool Mine button when the payout address is empty
("enter a payout address first"), and trim the pool URL/worker on persist.
- Console: track in-flight RPC commands (atomic counter + busy() override) so the
input is disabled while a command runs instead of piling up on the worker.
- Maintenance (rescan/repair/delete-chain/reinstall-daemon): re-entrancy guard —
bail with "already in progress" instead of launching a duplicate destructive op.
- Bootstrap dialog: re-attach to an in-flight download on reopen instead of
resetting state and orphaning the running worker.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2374,13 +2374,26 @@ void App::exportAllKeys(std::function<void(const std::string&, int, int)> callba
|
||||
}
|
||||
}
|
||||
|
||||
void App::importPrivateKey(const std::string& key, std::function<void(bool, const std::string&)> callback)
|
||||
void App::importPrivateKey(const std::string& rawKey, std::function<void(bool, const std::string&)> callback)
|
||||
{
|
||||
if (!state_.connected || !rpc_ || !worker_) {
|
||||
if (callback) callback(false, "Not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
// Trim whitespace/newlines — manual entry doesn't go through the dialog's Paste trimmer, and a
|
||||
// stray character makes the daemon reject the key with a cryptic error.
|
||||
std::string key(rawKey);
|
||||
while (!key.empty() && (key.front()==' '||key.front()=='\t'||key.front()=='\n'||key.front()=='\r')) key.erase(key.begin());
|
||||
while (!key.empty() && (key.back()==' '||key.back()=='\t'||key.back()=='\n'||key.back()=='\r')) key.pop_back();
|
||||
|
||||
// Reject anything that doesn't look like a Z/T private key before handing it to the daemon (the
|
||||
// dialog's indicator and this guard now share isRecognizedPrivateKey, so they can't disagree).
|
||||
if (!services::WalletSecurityController::isRecognizedPrivateKey(key)) {
|
||||
if (callback) callback(false, "Unrecognized private-key format.");
|
||||
return;
|
||||
}
|
||||
|
||||
const bool shielded = services::WalletSecurityController::classifyPrivateKey(key)
|
||||
== services::WalletSecurityController::KeyKind::Shielded;
|
||||
// Run on the worker thread — import requests a full rescan (rescan=true), so the
|
||||
|
||||
Reference in New Issue
Block a user