Compare commits

...

3 Commits

Author SHA1 Message Date
sami7777 a4da39f23c Update CI version to 5.1.9
Build All Platforms / build-windows-qt (push) Waiting to run
Build All Platforms / build-windows-daemon (push) Waiting to run
Build All Platforms / build-linux-qt (push) Waiting to run
Build All Platforms / build-linux-daemon (push) Waiting to run
Build All Platforms / build-macos (push) Waiting to run
Build All Platforms / release (push) Blocked by required conditions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 17:54:56 -07:00
sami7777 87bfc15712 Bump version to v5.1.9
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 17:48:24 -07:00
sami7777 6353e9d5fa v5.1.9: Assumevalid fast sync + unlimited network buffers
Major sync performance overhaul:

- Assumevalid: skip FetchInputs/ConnectInputs for blocks below
  checkpoint (2,186,940). Only write txindex entries. Eliminates
  millions of LevelDB reads during initial sync.
- Skip SyncWithWallets during IBD with automatic post-IBD wallet
  rescan from genesis and SecureMsg chain scan.
- Skip wallet best-chain locator update during IBD so restarts
  trigger proper rescan.
- Remove send/receive buffer limits (were 1MB/5MB, now unlimited).
  The 1MB send buffer was the root cause of ~180 block stalls -
  ProcessMessages stops reading when nSendSize >= SendBufferSize().
- Reduce IBD pipeline batch from 500 to 100 blocks for faster
  re-requesting with near-instant block processing.
- Seed getblocks limit raised to 20000 during IBD (was 500).
- Faster message handler polling during IBD (10ms vs 100ms).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 17:41:08 -07:00
9 changed files with 249 additions and 77 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ on:
workflow_dispatch:
env:
VERSION: "5.1.8"
VERSION: "5.1.9"
jobs:
build-windows-qt:
+3 -3
View File
@@ -3,7 +3,7 @@
# Generated by qmake (3.1) (Qt 5.15.18)
# Project: triangles-qt.pro
# Template: app
# Command: C:/msys64/mingw64/bin/qmake-qt5.exe -o Makefile triangles-qt.pro
# Command: C:/msys64/mingw64/bin/qmake-qt5.exe -o Makefile triangles-qt.pro USE_QRCODE=1 USE_UPNP=-
#############################################################################
MAKEFILE = Makefile
@@ -156,7 +156,7 @@ Makefile: triangles-qt.pro C:/msys64/mingw64/share/qt5/mkspecs/win32-g++/qmake.c
C:/msys64/mingw64/lib/qtmain.prl \
C:/msys64/mingw64/share/qt5/mkspecs/features/build_pass.prf \
src/qt/triangles.qrc
$(QMAKE) -o Makefile triangles-qt.pro
$(QMAKE) -o Makefile triangles-qt.pro USE_QRCODE=1 USE_UPNP=-
C:/msys64/mingw64/share/qt5/mkspecs/features/spec_pre.prf:
C:/msys64/mingw64/share/qt5/mkspecs/qdevice.pri:
C:/msys64/mingw64/share/qt5/mkspecs/features/device_config.prf:
@@ -244,7 +244,7 @@ C:/msys64/mingw64/lib/qtmain.prl:
C:/msys64/mingw64/share/qt5/mkspecs/features/build_pass.prf:
src/qt/triangles.qrc:
qmake: FORCE
@$(QMAKE) -o Makefile triangles-qt.pro
@$(QMAKE) -o Makefile triangles-qt.pro USE_QRCODE=1 USE_UPNP=-
qmake_all: FORCE
+1 -1
View File
@@ -8,7 +8,7 @@
// These need to be macros, as version.cpp's and triangles-qt.rc's voodoo requires it
#define CLIENT_VERSION_MAJOR 5
#define CLIENT_VERSION_MINOR 1
#define CLIENT_VERSION_REVISION 8
#define CLIENT_VERSION_REVISION 9
#define CLIENT_VERSION_BUILD 0
// Converts the parameter X to a string after macro replacement on X has been performed.
+2
View File
@@ -140,6 +140,7 @@ void Shutdown(void* parg)
if (fFirstThread)
{
fShutdown = true;
int64_t nDeferredWaitStart = GetTimeMillis();
while (true)
{
@@ -180,6 +181,7 @@ void Shutdown(void* parg)
fs::remove(GetPidFile());
UnregisterWallet(pwalletMain);
delete pwalletMain;
// DB is flushed and wallet saved - safe to force-exit if something hangs
NewThread(ExitTimeout, NULL);
MilliSleep(50);
printf("Triangles exited\n\n");
+126 -34
View File
@@ -1639,6 +1639,12 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
if (!CheckBlock(!fJustCheck, !fJustCheck, false))
return false;
// Determine if this block is covered by the hardcoded checkpoint.
// Below checkpoint: skip all input validation, FetchInputs, ConnectInputs,
// and wallet sync. The checkpoint hash guarantees chain integrity for these blocks.
bool fAssumeValid = (pindex->nHeight <= Checkpoints::GetTotalBlocksEstimate());
bool fIsInitialDownload = IsInitialBlockDownload();
//// issue here: it doesn't know the version
unsigned int nTxPos;
if (fJustCheck)
@@ -1658,6 +1664,20 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
{
uint256 hashTx = tx.GetHash();
CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
if (!fJustCheck)
nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
// Fast path: below checkpoint, skip all input validation and spent-tracking.
// Just record where each transaction lives on disk (txindex).
if (fAssumeValid)
{
mapQueuedChanges[hashTx] = CTxIndex(posThisTx, tx.vout.size());
continue;
}
// Full validation path (above checkpoint)
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
// unless those are already completely spent.
// If such overwrites are allowed, coinbases and transactions depending upon those
@@ -1681,10 +1701,6 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("ConnectBlock() : too many sigops"));
CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
if (!fJustCheck)
nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
MapPrevTx mapInputs;
if (tx.IsCoinBase())
nValueOut += tx.GetValueOut();
@@ -1717,20 +1733,18 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
mapQueuedChanges[hashTx] = CTxIndex(posThisTx, tx.vout.size());
}
if (IsProofOfWork() && pindex->nHeight > Checkpoints::GetTotalBlocksEstimate())
if (!fAssumeValid)
{
int64_t nReward = GetProofOfWorkReward(nFees);
// Check coinbase reward
if (vtx[0].GetValueOut() > nReward)
return DoS(50, error("ConnectBlock() : coinbase reward exceeded (actual=%"PRId64" vs calculated=%"PRId64")",
vtx[0].GetValueOut(),
nReward));
}
if (IsProofOfStake())
if (IsProofOfWork())
{
// Skip expensive coin age calculation and reward validation for blocks
// covered by the hardcoded checkpoint. The checkpoint guarantees chain integrity.
if (pindex->nHeight > Checkpoints::GetTotalBlocksEstimate())
int64_t nReward = GetProofOfWorkReward(nFees);
// Check coinbase reward
if (vtx[0].GetValueOut() > nReward)
return DoS(50, error("ConnectBlock() : coinbase reward exceeded (actual=%"PRId64" vs calculated=%"PRId64")",
vtx[0].GetValueOut(),
nReward));
}
if (IsProofOfStake())
{
// triangles: coin stake tx earns reward instead of paying fee
uint64_t nCoinAge;
@@ -1760,8 +1774,8 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
return error("ConnectBlock() : UpdateTxIndex failed");
}
// Update address index
if (fAddressIndex)
// Update address index (skip during IBD - will be rebuilt on next start with -reindex)
if (fAddressIndex && !fIsInitialDownload)
{
for (unsigned int i = 0; i < vtx.size(); i++)
{
@@ -1834,9 +1848,13 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
return error("ConnectBlock() : WriteBlockIndex failed");
}
// Watch for transactions paying to me
BOOST_FOREACH(CTransaction& tx, vtx)
SyncWithWallets(tx, this, true);
// Skip wallet sync during IBD - a full wallet rescan runs when IBD completes.
// This eliminates millions of per-transaction wallet lookups during sync.
if (!fIsInitialDownload)
{
BOOST_FOREACH(CTransaction& tx, vtx)
SyncWithWallets(tx, this, true);
}
return true;
}
@@ -2034,10 +2052,14 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
}
}
// Update best block in wallet (so we can detect restored wallets)
// Update best block in wallet (so we can detect restored wallets).
// During IBD, skip this so the wallet knows it needs rescanning on restart.
bool fIsInitialDownload = IsInitialBlockDownload();
const CBlockLocator locator(pindexNew);
::SetBestChain(locator);
if (!fIsInitialDownload)
{
const CBlockLocator locator(pindexNew);
::SetBestChain(locator);
}
// New best block
hashBestChain = hash;
@@ -2050,8 +2072,8 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
uint256 nBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust;
// Log every 500 blocks during sync, every block once caught up
if (nBestHeight % 500 == 0 || !IsInitialBlockDownload())
// Log every 5000 blocks during sync, every block once caught up
if (nBestHeight % 5000 == 0 || !IsInitialBlockDownload())
printf("SetBestChain: new best=%s height=%d trust=%s blocktrust=%"PRId64" date=%s\n",
hashBestChain.ToString().substr(0,20).c_str(), nBestHeight,
CBigNum(nBestChainTrust).ToString().c_str(),
@@ -2102,6 +2124,40 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
pNotificationQueue->Push(strBlockEvent);
}
// Detect IBD-to-synced transition and trigger deferred work:
// wallet rescan (since SyncWithWallets was skipped) and smsg chain scan.
{
static bool fWasInitialDownload = true;
if (fWasInitialDownload && !fIsInitialDownload)
{
printf("*** Initial block download complete at height %d ***\n", nBestHeight);
// Update wallet best chain locator now that IBD is done
const CBlockLocator locator(pindexBest);
::SetBestChain(locator);
// Wallet rescan: SyncWithWallets was skipped during IBD, so scan
// the entire chain to pick up all wallet transactions.
if (pwalletMain)
{
printf("Starting post-IBD wallet rescan from genesis...\n");
uiInterface.InitMessage(_("Rescanning wallet..."));
int nFound = pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true);
printf("Post-IBD wallet rescan complete: %d transactions found\n", nFound);
}
// Secure messaging: scan chain for public keys needed to decrypt messages
if (fSecMsgEnabled)
{
printf("Starting post-IBD secure message chain scan...\n");
uiInterface.InitMessage(_("Scanning for secure messages..."));
SecureMsgScanBlockChain();
printf("Post-IBD secure message chain scan complete\n");
}
}
fWasInitialDownload = fIsInitialDownload;
}
return true;
}
@@ -2598,7 +2654,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
mapOrphanBlocksByPrev.erase(hashPrev);
}
if (nBestHeight % 500 == 0 || !IsInitialBlockDownload())
if (nBestHeight % 5000 == 0 || !IsInitialBlockDownload())
printf("ProcessBlock: ACCEPTED block %d\n", nBestHeight);
// triangles: if responsible for sync-checkpoint send it
@@ -3211,13 +3267,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
}
}
// Ask the first connected node for block updates
// Ask connected nodes for block updates
// During IBD, always request blocks from any valid peer (critical for reconnection)
static int nAskedForBlocks = 0;
if (!pfrom->fClient && !pfrom->fOneShot &&
(pfrom->nStartingHeight > (nBestHeight - 144)) &&
(pfrom->nVersion < NOBLKS_VERSION_START ||
pfrom->nVersion >= NOBLKS_VERSION_END) &&
(nAskedForBlocks < 1 || vNodes.size() <= 1))
(IsInitialBlockDownload() || nAskedForBlocks < 1 || vNodes.size() <= 1))
{
nAskedForBlocks++;
pfrom->PushGetBlocks(pindexBest, uint256(0));
@@ -3464,7 +3521,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
// Send the rest of the chain
if (pindex)
pindex = pindex->pnext;
int nLimit = 500;
int nLimit = IsInitialBlockDownload() ? 20000 : 500;
printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit);
for (; pindex; pindex = pindex->pnext)
{
@@ -3674,19 +3731,35 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
vRecv >> block;
uint256 hashBlock = block.GetHash();
printf("received block %s\n", hashBlock.ToString().substr(0,20).c_str());
if (!IsInitialBlockDownload())
printf("received block %s\n", hashBlock.ToString().substr(0,20).c_str());
// block.print();
CInv inv(MSG_BLOCK, hashBlock);
pfrom->AddInventoryKnown(inv);
if (ProcessBlock(pfrom, &block))
{
mapAlreadyAskedFor.erase(inv);
// During IBD, proactively request more blocks to keep the pipeline full.
// With assumevalid, blocks process near-instantly, so request aggressively.
if (IsInitialBlockDownload())
{
static int nBlocksSinceRequest = 0;
if (++nBlocksSinceRequest >= 100)
{
nBlocksSinceRequest = 0;
pfrom->pindexLastGetBlocksBegin = NULL; // clear dedup filter
pfrom->PushGetBlocks(pindexBest, uint256(0));
}
}
}
if (block.nDoS)
pfrom->Misbehaving(block.nDoS);
if (fSecMsgEnabled)
if (fSecMsgEnabled && !IsInitialBlockDownload())
SecureMsgScanBlock(block);
}
@@ -4120,6 +4193,25 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
pto->PushMessage("inv", vInv);
//
// Stall detection: if IBD and no new blocks for 30 seconds, re-request
//
if (IsInitialBlockDownload() && !pto->fClient)
{
static int64_t nLastBlockReceived = 0;
static int nLastHeight = 0;
if (nBestHeight > nLastHeight) {
nLastHeight = nBestHeight;
nLastBlockReceived = GetTime();
} else if (nLastBlockReceived > 0 && GetTime() - nLastBlockReceived > 5) {
printf("IBD stall detected at height %d, re-requesting blocks\n", nBestHeight);
// Clear dedup filter so the request actually sends
pto->pindexLastGetBlocksBegin = NULL;
pto->PushGetBlocks(pindexBest, uint256(0));
nLastBlockReceived = GetTime(); // reset timer
}
}
//
// Message: getdata
//
+47 -20
View File
@@ -6,6 +6,7 @@
#include "irc.h"
#include "db.h"
#include "net.h"
#include "main.h"
#include "init.h"
#include "strlcpy.h"
#include "addrman.h"
@@ -889,7 +890,7 @@ void ThreadSocketHandler2(void* parg)
//
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 50000; // frequency to poll pnode->vSend
timeout.tv_usec = IsInitialBlockDownload() ? 10000 : 50000; // faster polling during IBD
fd_set fdsetRecv;
fd_set fdsetSend;
@@ -1574,24 +1575,29 @@ void ThreadOpenConnections2(void* parg)
if (fShutdown)
return;
// Add seed nodes if IRC isn't working
if (addrman.size()==0 && (GetTime() - nStart > 60) && !fTestNet)
// Add hardcoded seed nodes when we have no connections.
// Original check (addrman.size()==0) was too conservative - stale entries
// in peers.dat would prevent fallback to working hardcoded IPs forever.
{
std::vector<CAddress> vAdd;
for (unsigned int i = 0; i < ARRAYLEN(pnSeed); i++)
{
// It'll only connect to one or two seed nodes because once it connects,
// it'll get a pile of addresses with newer timestamps.
// Seed nodes are given a random 'last seen time' of between one and two
// weeks ago.
const int64_t nOneWeek = 7*24*60*60;
struct in_addr ip;
memcpy(&ip, &pnSeed[i], sizeof(ip));
CAddress addr(CService(ip, GetDefaultPort()));
addr.nTime = GetTime()-GetRand(nOneWeek)-nOneWeek;
vAdd.push_back(addr);
LOCK(cs_vNodes);
bool fNoOutbound = true;
BOOST_FOREACH(CNode* pnode, vNodes) {
if (!pnode->fInbound) { fNoOutbound = false; break; }
}
if (fNoOutbound && (GetTime() - nStart > 30) && !fTestNet)
{
std::vector<CAddress> vAdd;
for (unsigned int i = 0; i < ARRAYLEN(pnSeed); i++)
{
struct in_addr ip;
memcpy(&ip, &pnSeed[i], sizeof(ip));
CAddress addr(CService(ip, GetDefaultPort()));
addr.nTime = GetTime() - GetRand(60*60); // seen recently
vAdd.push_back(addr);
}
addrman.Add(vAdd, CNetAddr("127.0.0.1"));
printf("No outbound connections after 30s, added %d hardcoded seeds\n", (int)vAdd.size());
}
addrman.Add(vAdd, CNetAddr("127.0.0.1"));
}
//
@@ -1809,6 +1815,7 @@ void ThreadMessageHandler2(void* parg)
{
printf("ThreadMessageHandler started\n");
SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);
bool fWasBoosted = false;
while (!fShutdown)
{
vector<CNode*> vNodesCopy;
@@ -1851,11 +1858,21 @@ void ThreadMessageHandler2(void* parg)
pnode->Release();
}
// Boost thread priority during IBD, restore when caught up
if (IsInitialBlockDownload() && !fWasBoosted) {
SetThreadPriority(THREAD_PRIORITY_NORMAL);
fWasBoosted = true;
} else if (!IsInitialBlockDownload() && fWasBoosted) {
SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);
fWasBoosted = false;
}
// Wait and allow messages to bunch up.
// During IBD, use a shorter sleep to maximize block processing throughput.
// Reduce vnThreadsRunning so StopNode has permission to exit while
// we're sleeping, but we must always check fShutdown after doing this.
vnThreadsRunning[THREAD_MESSAGEHANDLER]--;
MilliSleep(100);
MilliSleep(IsInitialBlockDownload() ? 10 : 100);
if (fRequestShutdown)
StartShutdown();
vnThreadsRunning[THREAD_MESSAGEHANDLER]++;
@@ -2149,8 +2166,18 @@ bool StopNode()
if (vnThreadsRunning[THREAD_ADDEDCONNECTIONS] > 0) printf("ThreadOpenAddedConnections still running\n");
if (vnThreadsRunning[THREAD_DUMPADDRESS] > 0) printf("ThreadDumpAddresses still running\n");
if (vnThreadsRunning[THREAD_STAKE_MINER] > 0) printf("ThreadStakeMiner still running\n");
while (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0 || vnThreadsRunning[THREAD_RPCHANDLER] > 0)
MilliSleep(20);
{
int64_t nWaitStart = GetTime();
while (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0 || vnThreadsRunning[THREAD_RPCHANDLER] > 0)
{
if (GetTime() - nWaitStart > 10)
{
printf("Timed out waiting for message/RPC threads to stop\n");
break;
}
MilliSleep(20);
}
}
MilliSleep(50);
DumpAddresses();
return true;
+9 -4
View File
@@ -22,12 +22,13 @@
class CRequestTracker;
class CNode;
class CBlockIndex;
bool IsInitialBlockDownload();
extern int nBestHeight;
inline unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); }
inline unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); }
inline unsigned int ReceiveFloodSize() { return (unsigned int)-1; }
inline unsigned int SendBufferSize() { return (unsigned int)-1; }
void AddOneShot(std::string strDest);
bool RecvLine(SOCKET hSocket, std::string& strLine);
@@ -429,8 +430,12 @@ public:
nNow = std::max(nNow, nLastTime);
nLastTime = nNow;
// Each retry is 2 minutes after the last
nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow);
// During IBD, request immediately (no 2-minute retry delay)
// Normal operation: each retry is 2 minutes after the last
if (nRequestTime > 0 && IsInitialBlockDownload())
nRequestTime = nNow;
else
nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow);
mapAskFor.insert(std::make_pair(nRequestTime, inv));
}
+3 -1
View File
@@ -63,7 +63,9 @@ void ClientModel::updateTimer()
int newNumBlocks = getNumBlocks();
int newNumBlocksOfPeers = getNumBlocksOfPeers();
if(cachedNumBlocks != newNumBlocks || cachedNumBlocksOfPeers != newNumBlocksOfPeers)
// Always emit during IBD so the speed/ETA display stays live
if(cachedNumBlocks != newNumBlocks || cachedNumBlocksOfPeers != newNumBlocksOfPeers
|| newNumBlocks < newNumBlocksOfPeers)
{
cachedNumBlocks = newNumBlocks;
cachedNumBlocksOfPeers = newNumBlocksOfPeers;
+57 -13
View File
@@ -751,24 +751,57 @@ void TrianglesGUI::setNumBlocks(int count, int nTotalBlocks)
QString tooltip;
QString importText;
importText = tr("Synchronizing with network...");
if(count < nTotalBlocks)
{
// Calculate blocks/sec - only update rate when new blocks arrive
static int lastCount = 0;
static qint64 lastRateTime = 0;
static float blocksPerSec = 0.0f;
qint64 now = QDateTime::currentMSecsSinceEpoch();
if (count > lastCount) {
// New blocks arrived - recalculate speed
if (lastRateTime > 0) {
float elapsed = (now - lastRateTime) / 1000.0f;
if (elapsed > 0.1f) {
float instantRate = (count - lastCount) / elapsed;
blocksPerSec = (blocksPerSec < 0.1f) ? instantRate : (blocksPerSec * 0.7f + instantRate * 0.3f);
}
}
lastCount = count;
lastRateTime = now;
} else if (lastRateTime > 0 && (now - lastRateTime) > 10000) {
// No blocks for 10+ seconds - show 0
blocksPerSec = 0.0f;
}
int nRemainingBlocks = nTotalBlocks - count;
float nPercentageDone = count / (nTotalBlocks * 0.01f);
progressBarLabel->setText(importText);
// Build informative status text
QString speedText;
if (blocksPerSec >= 1.0f) {
int etaSeconds = (int)(nRemainingBlocks / blocksPerSec);
QString etaStr;
if (etaSeconds < 60)
etaStr = tr("%n sec", "", etaSeconds);
else if (etaSeconds < 3600)
etaStr = tr("%n min", "", etaSeconds / 60);
else
etaStr = tr("%1h %2m").arg(etaSeconds / 3600).arg((etaSeconds % 3600) / 60);
speedText = tr("Syncing: %1 blk/s ~%2 remaining").arg(blocksPerSec, 0, 'f', 1).arg(etaStr);
} else {
speedText = tr("Synchronizing with network...");
}
progressBarLabel->setText(speedText);
progressBarLabel->setVisible(true);
progressBar->setFormat(tr("~%n block(s) remaining", "", nRemainingBlocks));
progressBar->setFormat(tr("Block %1 / %2 (%3%)").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2));
progressBar->setMaximum(nTotalBlocks);
progressBar->setValue(count);
progressBar->setVisible(true);
ui->label_blocks->setText(tr("%n blocks", "", count));
ui->label_blocks->setVisible(true);
ui->label_blocks->setVisible(false);
tooltip = tr("Downloaded %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2);
tooltip = tr("Downloaded %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2);
}
else
{
@@ -871,16 +904,27 @@ void TrianglesGUI::changeEvent(QEvent *e)
void TrianglesGUI::closeEvent(QCloseEvent *event)
{
#ifndef Q_OS_MAC // Ignored on Mac
if(clientModel)
{
#ifndef Q_OS_MAC // Ignored on Mac
if(!clientModel->getOptionsModel()->getMinimizeToTray() &&
!clientModel->getOptionsModel()->getMinimizeOnClose())
if(clientModel->getOptionsModel()->getMinimizeOnClose())
{
QApplication::quit();
// Minimize to taskbar instead of closing
QMainWindow::showMinimized();
event->ignore();
return;
}
if(clientModel->getOptionsModel()->getMinimizeToTray() && trayIcon)
{
// Hide to system tray instead of closing
hide();
event->ignore();
return;
}
#endif
}
#endif
// Actually closing - quit the application
QApplication::quit();
QMainWindow::closeEvent(event);
}