feat(wallet): persist history and surface pending sends

Add an encrypted SQLite transaction history cache with cached tip metadata and
per-address shielded scan progress so startup and full refreshes avoid
re-scanning every z-address while still invalidating on wallet/address/rescan
changes.

Improve wallet history loading by paging transparent transactions, preserving
cached shielded and sent rows, keeping recent/unconfirmed activity visible, and
classifying mining-address receives. Show z_sendmany opid sends immediately in
History and Overview, pin pending rows through refreshes, and apply optimistic
address/balance debits until opids resolve.

Add timestamped RPC console tracing by source/method without logging params or
results, reduce redundant refresh/RPC calls, and cache Explorer recent block
summaries in SQLite.

Expand focused tests for transaction cache encryption, scan-progress
persistence/invalidation, history preservation, operation-status parsing,
pending send visibility, and Explorer/RPC refresh behavior.
This commit is contained in:
2026-05-05 03:22:14 -05:00
parent 948ef419ac
commit 975743f754
43 changed files with 3732 additions and 702 deletions

View File

@@ -309,7 +309,7 @@ void ImportKeyDialog::render(App* app)
bool rescan = s_rescan;
int rescanHeight = s_rescan_height;
if (app->worker()) {
app->worker()->post([rpc = app->rpc(), keys, rescan, rescanHeight]() -> rpc::RPCWorker::MainCb {
app->worker()->post([app, rpc = app->rpc(), keys, rescan, rescanHeight]() -> rpc::RPCWorker::MainCb {
int imported = 0;
int failed = 0;
@@ -317,6 +317,7 @@ void ImportKeyDialog::render(App* app)
std::string keyType = detectKeyType(key);
try {
rpc::RPCClient::TraceScope trace("Settings / Import private keys");
if (keyType == "z-spending") {
// z_importkey "key" "yes"|"no" startheight
if (rescan && rescanHeight > 0) {
@@ -348,13 +349,14 @@ void ImportKeyDialog::render(App* app)
// single rescanblockchain from that height now.
if (rescan && rescanHeight > 0 && imported > 0) {
try {
rpc::RPCClient::TraceScope trace("Settings / Import private keys");
rpc->call("rescanblockchain", {rescanHeight});
} catch (...) {
// rescan failure is non-fatal; user can retry
}
}
return [imported, failed]() {
return [app, imported, failed]() {
s_imported_keys = imported;
s_failed_keys = failed;
s_importing = false;
@@ -363,6 +365,8 @@ void ImportKeyDialog::render(App* app)
imported, failed);
s_status = buf;
if (imported > 0) {
app->invalidateAddressValidationCache();
app->refreshNow();
Notifications::instance().success(TR("import_key_success"), 5.0f);
}
};