From eb6114ee19a06bf35a022c1ec5165c6e618bee8a Mon Sep 17 00:00:00 2001 From: DanS Date: Fri, 5 Jun 2026 13:10:56 -0500 Subject: [PATCH] feat(lite): wire send + new-address GUI to the lite controller (M3/M4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Route the existing receive/balance/send UI to the lite controller in lite builds, with no per-tab UI changes — the existing buttons just work: - App::createNewZAddress / createNewTAddress: lite branch calls lite_wallet_->newAddress() (synchronous local key derivation), injects the new address into WalletState so the UI selects it next frame, and invokes the receive-tab callback. Placed before the full-node !connected guard. - App::sendTransaction: lite branch builds a LiteSendRequest (DRGX -> zatoshis, memo; `from`/`fee` ignored since the backend selects inputs and adds the fee), fires the controller's async broadcast, and stashes the send_tab callback. - App::update: drains takeBroadcastResult() and delivers txid/error to the stored callback, so the send_tab's existing "sending.../sent" flow works unchanged. All branches guard on lite_wallet_ (null in full-node). Verified: lite app + test suite + full-node variant all build/link clean; hygiene clean. Backup/import UI (export seed/keys, import) is deferred — it needs new secret-display UI rather than an existing button. Co-Authored-By: Claude Opus 4.8 --- src/app.cpp | 8 +++++++ src/app.h | 3 +++ src/app_network.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/app.cpp b/src/app.cpp index 2e3e277..d8bcaa5 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -407,6 +407,14 @@ void App::update() if (lite_wallet_->takeRefreshedModel(liteModel)) { wallet::applyLiteRefreshModelToWalletState(liteModel, state_); } + // Deliver a completed async send/shield result to the waiting send_tab callback. + wallet::LiteBroadcastResult broadcast; + if (lite_wallet_->takeBroadcastResult(broadcast)) { + if (lite_send_callback_) { + lite_send_callback_(broadcast.ok, broadcast.ok ? broadcast.txid : broadcast.error); + lite_send_callback_ = nullptr; + } + } } async_tasks_.reapCompleted(); diff --git a/src/app.h b/src/app.h index 2abb5e7..3cb36bc 100644 --- a/src/app.h +++ b/src/app.h @@ -419,6 +419,9 @@ private: std::unique_ptr settings_; std::unique_ptr lite_wallet_; // lite builds w/ linked backend + // Pending send_tab callback for an in-flight lite send (delivered in update() once the + // controller's async broadcast result arrives). Only one lite send runs at a time. + std::function lite_send_callback_; std::unique_ptr daemon_controller_; std::unique_ptr xmrig_manager_; util::AsyncTaskManager async_tasks_; diff --git a/src/app_network.cpp b/src/app_network.cpp index 0359aed..60ed328 100644 --- a/src/app_network.cpp +++ b/src/app_network.cpp @@ -34,6 +34,7 @@ #include "rpc/rpc_worker.h" #include "rpc/connection.h" #include "config/settings.h" +#include "wallet/lite_wallet_controller.h" // lite send/new-address routing #include "daemon/daemon_controller.h" #include "daemon/embedded_daemon.h" #include "daemon/xmrig_manager.h" @@ -1609,6 +1610,23 @@ void App::applyDefaultBanlist() void App::createNewZAddress(std::function callback) { + // Lite build: derive locally via the controller (fast, no network). The backend auto-saves + // new addresses; the next lite refresh lists it with a balance. + if (lite_wallet_) { + const auto result = lite_wallet_->newAddress(/*shielded*/ true); + if (result.ok) { + AddressInfo info; + info.address = result.address; + info.type = "shielded"; + info.balance = 0.0; + state_.z_addresses.push_back(info); + state_.addresses.push_back(info); + address_list_dirty_ = true; + } + if (callback) callback(result.ok ? result.address : std::string()); + return; + } + if (!state_.connected || !rpc_ || !worker_) return; worker_->post([this, callback]() -> rpc::RPCWorker::MainCb { @@ -1640,6 +1658,22 @@ void App::createNewZAddress(std::function callback) void App::createNewTAddress(std::function callback) { + // Lite build: derive locally via the controller (see createNewZAddress). + if (lite_wallet_) { + const auto result = lite_wallet_->newAddress(/*shielded*/ false); + if (result.ok) { + AddressInfo info; + info.address = result.address; + info.type = "transparent"; + info.balance = 0.0; + state_.t_addresses.push_back(info); + state_.addresses.push_back(info); + address_list_dirty_ = true; + } + if (callback) callback(result.ok ? result.address : std::string()); + return; + } + if (!state_.connected || !rpc_ || !worker_) return; worker_->post([this, callback]() -> rpc::RPCWorker::MainCb { @@ -1944,10 +1978,28 @@ void App::backupWallet(const std::string& destination, std::function callback) { + // Lite build: route to the controller's async broadcast. `from`/`fee` are ignored — the + // backend selects inputs and adds the network fee itself. The result (txid/error) is + // delivered to `callback` from update() once takeBroadcastResult() yields it. + if (lite_wallet_) { + wallet::LiteSendRequest req; + wallet::LiteSendRecipient recipient; + recipient.address = to; + recipient.amountZatoshis = static_cast(std::llround(amount * 100000000.0)); + recipient.memo = memo; + req.recipients.push_back(std::move(recipient)); + if (!lite_wallet_->sendTransaction(req)) { + if (callback) callback(false, "A send is already in progress, or no wallet is open"); + return; + } + lite_send_callback_ = std::move(callback); // delivered from update() + return; + } + if (!state_.connected || !rpc_) { if (callback) callback(false, "Not connected"); return;