fix(lite): import-key fallback on mis-routed key + clamp shield fee

- importKey routed transparent vs. shielded purely by the first character, which
  can mis-route (e.g. testnet/regtest WIFs). On failure, try the other import
  command before reporting an error (each validates the encoding, so a wrong
  command rejects rather than mis-imports). The key copy is wiped after both tries.
- Clamp the shield dialog's fee input to [0, 1] DRGX, mirroring the UTXO-limit
  clamp, so a negative or fat-fingered huge fee can't be submitted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 14:25:44 -05:00
parent a605e35409
commit 8c2c1c2aaf
2 changed files with 32 additions and 17 deletions

View File

@@ -138,6 +138,8 @@ void ShieldDialog::render(App* app)
ImGui::Text("%s", TR("fee_label"));
ImGui::SetNextItemWidth(feeInput.width);
ImGui::InputDouble("##Fee", &s_fee, 0.0001, 0.001, "%.8f");
if (s_fee < 0.0) s_fee = 0.0; // no negative fee
if (s_fee > 1.0) s_fee = 1.0; // guard a fat-fingered huge fee (mirrors utxo clamp)
ImGui::SameLine();
ImGui::TextDisabled("DRGX");

View File

@@ -465,27 +465,40 @@ LiteImportResult LiteWalletController::importKey(std::string spendingOrViewingKe
return out;
}
// Transparent WIFs begin with U/5/K/L (TImportCommand); shielded keys begin with
// "secret-..." / viewing keys "zxview...", so this prefix check won't collide.
// "secret-..." / viewing keys "zxview...", so this prefix check usually won't collide.
const char first = spendingOrViewingKey[0];
const bool transparentWif = (first == 'U' || first == '5' || first == 'K' || first == 'L');
const auto result = bridge_->execute(transparentWif ? "timport" : "import", spendingOrViewingKey);
secureWipeLiteSecret(spendingOrViewingKey); // wipe our copy ASAP
const bool transparentFirst = (first == 'U' || first == '5' || first == 'K' || first == 'L');
if (!result.ok) { // do_import_* failures come back "Error:"-prefixed (bridge -> ok=false)
out.error = result.error.empty() ? "key import failed" : result.error;
return out;
}
try {
const auto j = nlohmann::json::parse(result.value);
if (j.is_object() && j.contains("error")) {
out.error = extractJsonError(j);
return out;
auto runImport = [&](const char* command) -> LiteImportResult {
LiteImportResult r;
const auto result = bridge_->execute(command, spendingOrViewingKey);
if (!result.ok) { // do_import_* failures come back "Error:"-prefixed (bridge -> ok=false)
r.error = result.error.empty() ? "key import failed" : result.error;
return r;
}
} catch (...) {
// A non-JSON success payload is acceptable; fall through.
try {
const auto j = nlohmann::json::parse(result.value);
if (j.is_object() && j.contains("error")) {
r.error = extractJsonError(j);
return r;
}
} catch (...) {
// A non-JSON success payload is acceptable; fall through.
}
r.detail = result.value;
r.ok = true;
return r;
};
out = runImport(transparentFirst ? "timport" : "import");
if (!out.ok) {
// The single-char heuristic can mis-route (e.g. testnet/regtest WIFs). Try the other
// command before giving up — the wrong command rejects the key (each validates the
// encoding), it never imports it as the wrong type.
LiteImportResult alt = runImport(transparentFirst ? "import" : "timport");
if (alt.ok) out = alt;
}
out.detail = result.value;
out.ok = true;
secureWipeLiteSecret(spendingOrViewingKey); // wipe our copy after both attempts
return out;
}