fix(core): refresh transaction history on the Mining tab
The Mining tab shows earnings derived from transaction history, but it was not treated as a wallet-data page, so newly mined or paid-out earnings did not appear until the user left the tab and came back. A coinbase row also changes category in place (immature becomes generate at maturity, or orphan on a reorg), and the Recent merge kept both the old and the new row, double-counting the earnings. - The Mining tab now gets the cheap between-blocks Recent poll: one listtransactions per new block, bounded by the 15 s transaction-age timer, once history has loaded. It never runs a full routine scan and never pre-empts an owed dirty cycle; tracked sends still take their normal cycle on any page. - The shielded-payout probe runs only while the pool miner is running, since pool payouts are shielded receives while a solo miner's earnings are transparent coinbase rows. - The Recent merge drops the previous row of the other coinbase category when a coinbase transitions, so maturing or reorged rewards are no longer counted twice. A non-coinbase row sharing the txid is left alone. Behaviour change: mining earnings update on the Mining tab per block instead of only on tab re-entry, and maturing or reorged coinbase rows no longer double-count. Tests: the per-block poll gate (one poll per block, due only after history loads, held while dirty or addresses are dirty), the Mining-page cycle shaping, and the coinbase transition merge for immature to generate, generate to orphan, and a shared-txid non-coinbase row. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+27
-2
@@ -886,6 +886,7 @@ void App::onDisconnected(const std::string& reason)
|
||||
confirmed_tx_ids_.clear();
|
||||
confirmed_cache_block_ = -1;
|
||||
last_tx_block_height_ = -1;
|
||||
mining_tx_poll_block_ = -1;
|
||||
pending_opids_.clear();
|
||||
pending_send_info_.clear();
|
||||
// Resolve any deferred send callbacks so their UI doesn't spin forever on disconnect.
|
||||
@@ -1207,6 +1208,7 @@ bool App::currentPageNeedsWalletDataRefresh() const
|
||||
bool App::shouldRunWalletTransactionRefresh() const
|
||||
{
|
||||
if (currentPageNeedsWalletDataRefresh()) return true;
|
||||
if (current_page_ == ui::NavPage::Mining) return true; // C119: refreshTransactionData() narrows it to the Recent poll
|
||||
if (!send_txids_.empty()) return true;
|
||||
return transactions_dirty_;
|
||||
}
|
||||
@@ -2637,13 +2639,32 @@ bool App::refreshTransactionData()
|
||||
in.routineScanWork = work.routineWork;
|
||||
in.syncing = effectivelySyncing();
|
||||
in.heightKnown = tip > 0;
|
||||
in.recentDue = currentPageNeedsWalletDataRefresh() && shouldRefreshRecentTransactions();
|
||||
const bool walletPage = currentPageNeedsWalletDataRefresh();
|
||||
// C119: the Mining tab shows history-derived earnings but only gets the cheap per-block Recent poll.
|
||||
const bool miningPoll = current_page_ == ui::NavPage::Mining && !walletPage;
|
||||
in.recentDue = walletPage && shouldRefreshRecentTransactions();
|
||||
if (miningPoll) {
|
||||
using Refresh = services::NetworkRefreshService;
|
||||
in = services::shapeTxCycleForMiningPage(in);
|
||||
services::MiningTxPollInputs mp;
|
||||
mp.tip = tip;
|
||||
mp.lastPolledTip = mining_tx_poll_block_;
|
||||
mp.txAgeDue = network_refresh_.isDue(Refresh::Timer::TxAge);
|
||||
mp.historyLoaded = last_tx_block_height_ >= 0;
|
||||
mp.dirty = transactions_dirty_;
|
||||
mp.addressesDirty = addresses_dirty_;
|
||||
in.recentDue = services::miningTxPollDue(mp);
|
||||
}
|
||||
// What this cycle is without and with a routine wallet-wide scan: the scan budget (the governor)
|
||||
// decides between the two.
|
||||
in.gateOpen = false;
|
||||
const TxCycle held = services::decideTxCycle(in);
|
||||
in.gateOpen = true;
|
||||
const TxCycle open = services::decideTxCycle(in);
|
||||
TxCycle open = services::decideTxCycle(in);
|
||||
// Pool payouts are shielded receives (a z_listreceivedbyaddress probe finds them); a solo miner's earnings
|
||||
// are transparent coinbase rows, so on the Mining tab the probe runs only while the pool miner is.
|
||||
if (miningPoll && open == TxCycle::RecentWithProbe && !(xmrig_manager_ && xmrig_manager_->isRunning()))
|
||||
open = TxCycle::Recent;
|
||||
|
||||
const double now = scanClockNow();
|
||||
const auto sig = currentScanSignals();
|
||||
@@ -2669,9 +2690,13 @@ bool App::refreshTransactionData()
|
||||
return heldBack;
|
||||
case TxCycle::Recent:
|
||||
startRecentTransactionPoll(false, {});
|
||||
if (miningPoll && network_refresh_.jobInProgress(services::NetworkRefreshService::Job::Transactions))
|
||||
mining_tx_poll_block_ = tip; // dispatched (the job guard was clear on entry): one poll per block
|
||||
return heldBack;
|
||||
case TxCycle::RecentWithProbe:
|
||||
startRecentTransactionPoll(true, ticket);
|
||||
if (miningPoll && network_refresh_.jobInProgress(services::NetworkRefreshService::Job::Transactions))
|
||||
mining_tx_poll_block_ = tip;
|
||||
return false;
|
||||
case TxCycle::Full:
|
||||
case TxCycle::Light:
|
||||
|
||||
Reference in New Issue
Block a user