#pragma once #include #include #include #include #include "data/wallet_state.h" // TransactionInfo namespace dragonx { namespace ui { int GetMaxMiningThreads(); int ClampMiningThreads(int requestedThreads, int maxThreads); bool IsPoolMiningActive(bool poolMode, bool xmrigRunning, bool soloMiningRunning); std::string FormatHashrate(double hashrate); std::string FormatFeePercent(double feePercent); double EstimateHoursToBlock(double localHashrate, double networkHashrate, double difficulty); std::string FormatEstTime(double estimatedHours); // ---- Mining earnings card (Today / Yesterday / All time + the 4 most recent rows) ---- // Stored BY VALUE: never point into WalletState::transactions (a refresh replaces / reallocates it). struct MiningEarningsRecentTx { std::int64_t timestamp = 0; double amount = 0.0; int confirmations = 0; bool mature = false; std::string txid; bool isPoolPayout = false; }; struct MiningEarningsTotals { double today = 0.0, yesterday = 0.0, allTime = 0.0; int todayCount = 0, yesterdayCount = 0, allTimeCount = 0; std::vector recent; // <= 4, in tx-list order }; // The earnings card's former inline loop, verbatim. filter: 0 All, 1 Solo, 2 Pool (the card's filter pill). MiningEarningsTotals ComputeMiningEarnings(const std::vector& txs, int filter, bool soloSupported, bool poolMode, std::int64_t todayStart, std::int64_t yesterdayStart); // Every input the totals depend on: equal keys => the cached totals are still valid. struct MiningEarningsKey { std::uint64_t txRevision = 0; // WalletState::tx_revision (bumped by every write to `transactions`) std::size_t txCount = 0; // belt: a future writer that skips the bump but changes the row count int filter = 0; bool soloSupported = false; bool poolMode = false; // picks the recent-4 rows (solo blocks vs pool payouts) std::int64_t todayStart = 0; // local midnight; yesterday = todayStart - 86400, as before bool operator==(const MiningEarningsKey& o) const { return txRevision == o.txRevision && txCount == o.txCount && filter == o.filter && soloSupported == o.soloSupported && poolMode == o.poolMode && todayStart == o.todayStart; } }; class MiningEarningsCache { public: static constexpr std::int64_t kMaxAgeMs = 2000; // bounds staleness if a future writer skips the bump const MiningEarningsTotals& get(const MiningEarningsKey& key, const std::vector& txs, std::int64_t nowMs); int computeCount() const { return computes_; } // tests private: bool valid_ = false; MiningEarningsKey key_; std::int64_t computedAtMs_ = 0; MiningEarningsTotals totals_; int computes_ = 0; }; // True when a sample taken at lastMs is at least minMs old (or the clock went backwards). bool IntervalElapsed(std::int64_t nowMs, std::int64_t lastMs, std::int64_t minMs); // Local midnight for `now` (the same localtime + mktime as before). A pure function of the second, so it is // recomputed only when `now` changes instead of on every frame (bit-identical result, DST quirks included). class LocalDayStartMemo { public: std::int64_t get(std::int64_t now); private: std::int64_t now_ = INT64_MIN, start_ = 0; }; } // namespace ui } // namespace dragonx